Présentation de l interface MultiTouch (MT) PJE Multi-touch Fabrice Aubert fabrice.aubert@lifl.fr Master Info - Parcours IVI UFR IEEA 2014-2015 F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 1 / 32
Outils de développement utilisés Développement en Java Utilisation de Swing pour l interface Utilisation d un client TUIO pour les couches basses (connexion UDP, etc) Utilisation de Java2D pour la visualisation F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 2 / 32
Objectifs du toolkit MultiTouch I Structure logicielle calquée sur les librairies d interfaces usuelles (Component, Container, Widget, Programmation événementielle). I Analyse du geste d interaction (translate/rotate/scale, one dollar, axe principaux). I Illustration de ces concepts avec un visualiseur d images. F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 3 / 32
1 Java et Swing F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 4 / 32
Hello World Cours JAVA (POO : LS4, COO : LS5) : http://www.fil.univ-lille1.fr/portail/ Tutorial SWING : http://java.sun.com/docs/books/tutorial/uiswing/ import javax. swing. ; import java. awt. ; import java. awt. event. ; public class Main { s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ; f. setpreferredsize (new Dimension (5 0 0,5 0 0 ) ) ; f. pack ( ) ; f. s e t V i s i b l e ( true ) ; s t a t i c public void main ( String arg [ ] ) {... F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 5 / 32
Event Dispatch Thread La gestion des événements SWING est réalisée dans un thread appelé Event Dispatch Thread. Différent du Initial Thread qui correspond à l exécution du static public main() {. Il faut s assurer que tout ce qui concerne la gestion de l interface est faite dans le Event Dispatch Thread invokelater : import javax. swing. ; import java. awt. ; import java. awt. event. ; public class Main { s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ;... f. s e t V i s i b l e ( true ) ; s t a t i c public void main ( String arg [ ] ) { SwingUtilities. invokelater (new Runnable ( ) { public void run ( ) { createandshowgui ( ) ; ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 6 / 32
JFrame = Top-Level Container Component : brique de base de l interface (= widgets : boutons, listes déroulantes, etc). Container : ensemble (ou groupe) de composants de l interface graphique (pour organiser l interface en sous ensembles ; boutons radios ;...). Un container est lui-même un composant possibilité d organiser l interface hiérarchiquement. La racine de la structure de l interface (Top-Level Container) est gérée par la classe JFrame : d un container (container racine de tous les composants de l interface) : contentpane. du fenêtrage système (fermeture, iconification, look and feel). de la possibilité d ajouter un menu. F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 7 / 32
Ajouter des composants à un container s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ; f. setlayout (new FlowLayout ( ) ) ; f. setpreferredsize (new Dimension (5 0 0,5 0 0 ) ) ; JButton button1=new JButton ( "Bouton 1 " ) ; button1. setpreferredsize (new Dimension (1 0 0,1 0 0 ) ) ; f. getcontentpane ( ). add ( button1 ) ; f. pack ( ) ; f. s e t V i s i b l e ( true ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 8 / 32
Layout Manager Tout container possède un «Layout Manager» : fixe la stratégie de positionnement des composants. La taille et la position des composants sont fixés au moment du f.pack() qui respecte au mieux le layout manager. Exemple : JFrame avec un FlowLayout, et 2 boutons dans le conteneur (remarquer les setpreferredsize) s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ; f. setpreferredsize (new Dimension (5 0 0,5 0 0 ) ) ; f. setlayout (new FlowLayout ( ) ) ; JButton button1=new JButton ( " button1 " ) ; button1. setpreferredsize (new Dimension (1 0 0,3 0 ) ) ; f. getcontentpane ( ). add ( button1 ) ; JButton button2=new JButton ( " button2 " ) ; button2. setpreferredsize (new Dimension (2 0 0,5 0 ) ) ; f. getcontentpane ( ). add ( button2 ) ; f. pack ( ) ; f. s e t V i s i b l e ( true ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 9 / 32
Evénement L interface doit réagir aux événements utilisateurs (click sur un JButton par exemple). Programmation événementielle : Il faut indiquer à SWING quelle est la méthode à appeler dans le cas où un événement donné se produit pour un composant donné (méthode souvent appelée callback). A l exécution, SWING gère les messages (click, affichage d un composant, etc) reçus par les composants : SWING appelle alors le callback correspondant (mécanisme géré par la boucle d événement = Event Dispatch Thread). Pour spécifier les callbacks : mécanisme de Listener (=écouteur d événements).... JButton button1=new JButton ( "Bouton 1 " ) ; button1. setpreferredsize (new Dimension (1 0 0,1 0 0 ) ) ; button1. addactionlistener ( un_ecouteur ) ; / / un_ecouteur dé f i n i dans la suite du cours f. getcontentpane ( ). add ( button1 ) ;... a.addactionlistener(b) : l objet b écoute le composant a F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 10 / 32
ActionListener La classe de un_ecouteur doit implémenter l interface ActionListener. Méthode à implémenter : actionperformed. import java. awt. event. ; public class MyListener implements A c t i o n L i s t e n e r { / / classe de un_ecouteur public void actionperformed ( ActionEvent arg0 ) { System. out. p r i n t l n ( " Bouton c l i c k " ) ; public class Main { s t a t i c ActionListener un_ecouteur=new MyListener ( ) ; s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ;... button1. addactionlistener ( un_ecouteur ) ;...... F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 11 / 32
En plus direct... Avec une classe anonyme qui implémente l interface ActionListener : s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ; f. setlayout (new FlowLayout ( ) ) ; f. setpreferredsize (new Dimension (5 0 0,5 0 0 ) ) ; JButton button1=new JButton ( "Bouton 1 " ) ; button1. setpreferredsize (new Dimension (1 0 0,1 0 0 ) ) ; button1. addactionlistener (new A c t i o n L i s t e n e r ( ) { public void actionperformed ( ActionEvent e ) { actionbutton1 ( e ) ; ) ; f. getcontentpane ( ). add ( button1 ) ; f. pack ( ) ; f. s e t V i s i b l e ( true ) ; s t a t i c void actionbutton1 ( ActionEvent e ) { System. out. p r i n t l n ( " j appuie sur J1 " ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 12 / 32
Components/Containers Tous les composants et conteneurs héritent de JComponent Containers SWING utilisés pour le projet : JFrame (pour lancer l interface de l appli), JPanel pour faire un cadre et dessiner à l intérieur. Mais nous allons définir nos propres composants et containers (et donc leur mécanisme de listeners) : MTComponent : affichage = photo ; événements = message des curseurs (add, update, remove des doigts). MTSurface : initialisera et contiendra les MTComponent photos. F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 13 / 32
2 Créer ses propres listeners F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 14 / 32
Basé sur le mécanisme de SWING On veut retrouver ce style de code sur nos propres composants : / / L i s t e n e r d un c l i c k ( i.e. événement Action ) sur un bouton SWING : JButton b=new JButton ( " hello " ) ; b. addactionlistener (new A c t i o n L i s t e n e r ( ) { public void actionperformed ( ActionEvent evt ) { System. out. p r i n t l n ( " c l i c k sur h e l l o " ) ; ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 15 / 32
Exemple L exemple présenté s appuie sur un générateur de nombres aléatoires. Il génére un événement à chaque fois qu il s agit d un nombre aléatoire. On souhaite alors qu un objet quelconque puisse écouter (et réagir à) ces événements : 1 Définir la classe des événements générés (sera la classe ImpairEvent dans l exemple). 2 Gérer la génération des événements (procédure generate). 3 Inclure un gestionnaire des écouteurs pour les objets écoutables (classe Composant). 4 Faire l interface du listener (interface InterfaceListener). 5 Exploiter le mécanisme... F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 16 / 32
Exploiter le mécanisme On souhaite donc pouvoir faire le code suivant (à comparer avec l exemple du bouton swing) : / / L i s t e n e r d un nombre a l é a t o i r e ( i.e. événement Impair ) sur un o b j e t de classe Composant : Composant uncomposant=new Composant ( ) ; uncomposant. addimpairlistener (new I m p a i r L i s t e n e r ( ) { public void impairperformed ( ImpairEvent evt ) { System. out. p r i n t l n ( "Nombre Impair : "+evt. getvalue ( ) ) ; ) ; Pour mémoire (exemple du bouton SWING) : / / L i s t e n e r d un c l i c k ( i.e. événement Action ) sur un bouton SWING : JButton b=new JButton ( " hello " ) ; b. addactionlistener (new A c t i o n L i s t e n e r ( ) { public void actionperformed ( ActionEvent evt ) { System. out. p r i n t l n ( " c l i c k sur h e l l o " ) ; ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 17 / 32
La classe des événements générés Classe des événements (elle inclut toutes les infos utiles/nécessaires à l événement) : public class ImpairEvent extends AWTEvent { private double v ; public ImpairEvent ( Object source, i n t id, double value ) { super ( source, i d ) ; v=value ; public double getvalue ( ) { return v ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 18 / 32
La génération des événements A inclure dans une boucle infinie par exemple (thread qui génère les événements) : public void generate ( ) { int a=( int ) ( Math. random () 1000.0); i f ( a % 2==1) { ImpairEvent evt=new ImpairEvent (uncomposant,0,a ) ; uncomposant. fireimpairperformed ( evt ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 19 / 32
Interface du listener Inclut la déclaration de la méthode qui doit répondre à l event : public interface I m p a i r L i s t e n e r extends EventListener { public void impairperformed ( ImpairEvent evt ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 20 / 32
Les objets écoutables public class Composant extends JComponent { / / L i s t e d écouteurs : private E v e n t L i s t e n e r L i s t l i s t e n e r L i s t = null ; public Composant ( ) { l i s t e n e r L i s t =new EventListenerList ( ) ; / / a j o u t e r un é couteur public void addimpairlistener ( I m p a i r L i s t e n e r e ) { l i s t e n e r L i s t. add ( ImpairListener. class,e ) ; / / i n f o r m e r tous l e s é couteurs public void f i r e I m p a i r P e r f o r m e d ( AWTEvent event ) { Object [ ] l i s t e n e r s = l i s t e n e r L i s t. getlistenerlist ( ) ; for ( i n t i = listeners. length 1; i >=0; i ) { i f ( ( l i s t e n e r s [ i ] == ImpairListener. class ) ) ( ( ImpairListener ) listeners [ i +1]). impairperformed ( ( ImpairEvent ) event ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 21 / 32
3 Introduction à JAVA 2D F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 22 / 32
paintcomponent Tutoriel : http://java.sun.com/docs/books/tutorial/2d/ JComponent : méthode paintcomponent appelée à chaque événement repaint() (provoqué par le système, ou directement par le programmeur). Exemple : surcharge de JButton : s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ; f. setpreferredsize (new Dimension (5 0 0,5 0 0 ) ) ; import javax. swing. ; import java. awt. ; import java. awt.geom. ; CButton button1=new CButton ( "Bouton 1 " ) ;... public class CButton extends JButton { public CButton ( S t r i n g s ) { super ( s ) ; public void paintcomponent ( Graphics g ) { Graphics2D g2=( Graphics2D ) g ; g2. f i l l (new Ellipse2D. Double (3 0,3 0,4 0,40)); F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 23 / 32
Graphics2D paintcomponent(graphics g) : g = environnement graphique du composant Cast en Graphics2D pour bénéficier de toutes les fonctionnalités de Java2D Les méthodes de Graphics2D permettent de : lire/modifier les attributs de tracé (hauteur, largeur, couleur, épaisseur de traits, transformations...) : l ensemble des valeurs de ces attributs à un instant donné constitue l état courant du tracé. dessiner (rectangle, ellipse, polygones, images, textes...) dans l état courant. Repère initial : Pour connaitre la hauteur et la largeur (en pixels), utiliser getwidth() et getheight() du composant. F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 24 / 32
draw/fill draw(shape s) : trace une forme géométrique. fill(shape s) : remplit la forme géométrique. public class MyPanel extends JPanel { public MyPanel ( ) { super ( ) ; public void paintcomponent ( Graphics g ) { Graphics2D g2=( Graphics2D ) g ; g2. setcolor (new Color ( 200, 0, 0 ) ) ; g2. draw (new Rectangle2D. Double (10,10,100,100)); g2. setcolor (new Color ( 0, 0, 100)); g2. f i l l (new Rectangle2D. Double (150,10,100,100)); public class Main { s t a t i c public void createandshowgui ( ) { JFrame f =new JFrame ( " Hello World!!! " ) ; f. setlayout (new FlowLayout ( ) ) ; f. setpreferredsize (new Dimension (5 0 0,5 0 0 ) ) ; MyPanel panel=new MyPanel ( ) ; panel. s e t P r e f e r r e d S i z e (new Dimension ( 3 0 0, 3 0 0 ) ) ; panel. setborder (new LineBorder ( new Color (0, 200, 0 ), 3 ) ) ; f. getcontentpane ( ). add ( panel ) ; f. pack ( ) ; f. s e t V i s i b l e ( true ) ; s t a t i c public void main ( String arg [ ] ) { SwingUtilities. invokelater (new Runnable ( ) { public void run ( ) { createandshowgui ( ) ; ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 25 / 32
Remarques pour Graphics2D setcolor(new Color(r,v,b)) : fixe la couleur courante en composantes (rouge,vert,bleu) ( [0, 255]). new Rectangle2D.Double(x0,y0,width,height) : dérive de Shape pour JPanel : Container simple, sans particularité : très souvent utilisé pour faire un espace de dessin. setborder(new LineBorder(Color c, int thickness)) : permet de déssiner un bord. F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 26 / 32
Lignes Polygonales public void paintcomponent ( Graphics g ) { Graphics2D g2=( Graphics2D ) g ; GeneralPath p=new GeneralPath ( ) ; p. moveto(50, 200); p. lineto (150, 100); p. lineto (250, 200); / / p. closepath ( ) ; / / s i on souhaite fermer le path / / ( i. e. r e l i e r avec l e p o i n t i n i t i a l ) g2. draw (p ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 27 / 32
Tracer une image import java. io. ; import javax. swing. ; import javax. imageio. ; import java. awt. ; import java. awt. image. ; import java. awt.geom. ; public class MyPanel extends JPanel { BufferedImage img ; public MyPanel ( ) { super ( ) ; t r y { img=imageio. read ( new F i l e ( " data / univ l i l l e 1 _ p e t i t e. jpg " ) ) ; catch ( IOException e ) { e. p r i ntstacktrace ( ) ; public void paintcomponent ( Graphics g ) { super. paintcomponent (g ) ; Graphics2D g2=( Graphics2D ) g ; g2. drawimage (img,50,100, null ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 28 / 32
4 Fondations de l application MultiTouch F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 29 / 32
Vers un Widget Toolkit MTsurface : Top-Level container des composants multi-touch. hérite de JPanel pour être intégré à une application SWING. possèdera un unique MTcontainer (container racine). reçoit tous les messages de position des curseurs : c est MTsurface qui détermine à quel composant cela s adresse (fonctionnement simplifié par rapport à l edt de SWING). MTedt : reçoit les événements TUIO et les transmets à MTsurface (intermédiaire entre le serveur TUIO et MTsurface). F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 30 / 32
MTedt : lanceur et écouteur du client TUIO On utilise un client TUIO déjà implémenté en Java (http ://www.tuio.org/?software). classe TuioClient : assure la connexion UDP avec le serveur TUIO. classe TuioListener : interface pour écouter les événements TUIO. public class MTedt implements T u i o L i s t e n e r { T u i o C l i e n t c l i e n t = null ; MTSurface surface ; public MTedt ( MTSurface s ) { i n t p o r t =3333; c l i e n t =new T u i o C l i e n t ( p o r t ) ; client. addtuiolistener ( this ) ; c l i e n t. connect ( ) ; i f (! c l i e n t. isconnected ( ) ) { System. exit ( 1 ) ; System. out. p r i n t l n ( " connection " ) ; surface=s ;... F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 31 / 32
MTedt : réponses aux messages public class MTedt implements T u i o L i s t e n e r { T u i o C l i e n t c l i e n t = null ; MTSurface surface ;... public void addtuiocursor ( TuioCursor t c u r ) { / / System. out. p r i n t l n ( " add cur "+ t c u r. getcursorid ( ) + " ( " + t c u r. getsessionid ( ) + " ) "+ t c u r. getx ( ) + " "+ t c u r. gety ( ) ) ; surface. addcursor ( t c u r. getcursorid ( ), t c u r. getx ( ), t c u r. gety ( ) ) ; public void updatetuiocursor ( TuioCursor tcur ) { / / System. out. println ( " set cur "+ tcur. getcursorid ( )+" ( "+ tcur. getsessionid ( )+") "+ tcur. getx ( )+" "+ tcur. gety ( )+" "+ / / t c u r. getmotionspeed ( ) + " "+ t c u r. getmotionaccel ( ) ) ; surface. updatecursor ( t c u r. getcursorid ( ), t c u r. getx ( ), t c u r. gety ( ) ) ; public void removetuiocursor ( TuioCursor t c u r ) { / / System. out. p r i n t l n ( " del cur "+ t c u r. getcursorid ( ) + " ( " + t c u r. getsessionid ( ) + " ) " ) ; surface. removecursor ( t c u r. getcursorid ( ), t c u r. getx ( ), t c u r. gety ( ) ) ; F. Aubert (MS1) PJE MultiTouch/ Présentation de l interface MT 2014-2015 32 / 32