IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

EDT/SwingWorker Java Discussion :

Attendre la fin d'une animation pour éxécuter la suite du code


Sujet :

EDT/SwingWorker Java

  1. #1
    Membre à l'essai
    Homme Profil pro
    Ingénieur process
    Inscrit en
    Mars 2017
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur process
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2017
    Messages : 17
    Points : 17
    Points
    17
    Par défaut Attendre la fin d'une animation pour éxécuter la suite du code
    Bonjour,

    J'essaie de coder une suite d'animations, mais je n'arrive pas à les enchaîner. J'ai fait un exemple assez simple pour illustrer mon problème.

    Le principe de ce que je souhaite faire :
    Dans mon panel, je clique à un endroit (une flèche dessinée), ce qui fait "descendre" un petit disque du haut en bas de ce panel.
    Le programme fait ensuite descendre un second disque de haut en bas, à l'endroit qu'il aura choisi.

    Mon problème : le premier disque descend bien (la première animation fonctionne), mais pas le second.

    Voici mon code. J'ai codé mes classes pour essayer de suivre le pattern MVC.

    Mon modèle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
     
    public class Model extends AbstractModel {
    	protected static Model tmpModel;
    	protected boolean animation = false;
     
    	public Model(Color color1, Color color2) {
    		this.color1 = color1;
    		this.color2= color2;
    		for (int j = 0 ; j < 7 ; j++) {
    			column[j] = 0;
    		}
    	}
     
    	public void setAnimation (boolean animation) {
    		System.out.print("*** Méthode \"setAnimation\" de la classe Model ***");
    		System.out.println(" = > animation : " + animation);
    		this.animation = animation;
    	}
     
    	@Override
    	public int[] getColumn() {
    		return this.column;
    	}
     
    	@Override
    	public void setColumn(int[] column) {
    		this.column = column;
    	}
     
    	@Override
    	public Color getCurrentColor() {
    		return this.currentColor;
    	}
     
    	@Override
    	public void setCurrentColor(Color color) {
    		this.currentColor= color;
    	}
     
    	public void reset() {
    		for (int i = 0 ; i < 7 ; i ++) {
    			column[i] = 0;
    		}
    		this.removeObserver();
    	}
     
    	public Model getTmpModel() {
    		return Model.tmpModel;
    	}
     
    	public void setTmpModel(Model tmpModel) {
    		Model.tmpModel= tmpModel;
    	}
     
    	@Override
    	public void addObserver(Observer obs) {
    		this.listObserver.add(obs);
    	}
     
    	@Override
    	public void removeObserver() {
    		this.listObserver = new ArrayList<Observer>();
    	}
     
    	@Override
    	public void notifyObserver(Color color, int column) {
    		System.out.println("*** Méthode \"notifyObserver\" de la classe Model ***");
    		for (Observer obs : listObserver) {
    			obs.update(color, column);
    		}
    	}
     
    	@Override
    	public synchronized void move(Color color, int column, boolean mainModel) {
    		System.out.println("\n*** Méthode \"move\" de la classe Model ***");
    		System.out.println("\tDébut du Thread \"moveThread\"");
    		notifyObserver(color, column);
    		System.out.println("\tFin du Thread \"moveThread\"");
    		if (mainModel) {
    			currentColor = (currentColor == color1) ? color2 : color1;
    			int count = 0;
    			System.out.println("\tanimation : " + animation + " => " +
    					(animation == true ? "Début boucle d'attente" : "Pas de boucle d'attente"));
    			while (animation) {
    				try {
    					Thread.sleep(500);
    					count++;
    					System.out.println("Boucle d'attente n°" + count);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    			if (currentColor == color2) {
    				System.out.println("\tAppel à la méthode chooseNextMove");
    				this.chooseNextMove();
    			}
    		}
    	}
     
    	public void chooseNextMove() {
    		System.out.println("*** Méthode \"chooseNextMove\" de la classe Model ***");
    		this.move(currentColor, 3, true); //J'ai simplifié le code ici en choisissant l'entier 3. En réalité, il y a différents calculs à la place.
    	}
    }
    Mon controler :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    public class Controler extends AbstractControler {
    	protected Model model;
    	protected boolean animation = false;
     
    	public Controler(Model model) {
    		super(model);
    		this.model = model;
    	}
     
    	@Override
    	public void control(int i) {
    		this.model.move(this.model.getCurrentColor(), i, true);
    	}
     
    	public void setAnimation (boolean animation) {
    		System.out.print("*** Méthode \"setAnimation\" de la classe Controler ***");
    		System.out.println(" = > animation : " + animation);
    		this.animation = animation;
    		this.model.setAnimation(animation);
    	}
    }
    Ma frame :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    public class Frame extends JFrame implements Observer {
    	private JPanel container;
    	private Panel panel;
    	private Model model;
    	private Dimension dimension;
    	private String name;
     
    	public Frame(Model model, Controler controler) {
    		this.setSize(800, 800);
    		this.setLocationRelativeTo(null);
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		this.name = "Fenêtre";
    		this.model = model;
    		this.dimension = new Dimension(this.getWidth(), this.getHeight());
     
    		this.container = new JPanel();
    		this.container.setBackground(Color.white);
    		this.container.setPreferredSize(this.dimension);
    		panel = new Panel(controler);
    		this.container.add(panel, BorderLayout.CENTER);
    		this.setContentPane(this.container);
     
    		this.setVisible(true);
    	}
     
    	public Panel getPanel() {
    		return this.panel;
    	}
     
    	public String toString() {
    		return this.name;
    	}
     
    	@Override
    	public void update(Color color, int column) {
    		// TODO Auto-generated method stub
    	}
    }
    Mon panel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
     
    public class Panel extends JPanel implements Observer, MouseListener, MouseMotionListener {
    	private int length = 80;
    	private int gap = 6;
    	private Point2D origin = new Point2D.Double(20, 20);
    	private Ellipse2D movingPiece = new Ellipse2D.Double(); //disque en train de descendre
    	private Point2D movingPieceOrigin = new Point2D.Double(); //coordonnées origine du disque en train de descendre
    	private Color movingPieceColor; //Couleur du disque en train de descendre
    	private int column[] = new int[7];
    	private GeneralPath arrow[] = new GeneralPath[7];
    	private Point2D arrowOrigin[] = new Point2D[7];
    	private boolean isMoved[] = {false, false, false, false, false, false, false};
    	private boolean isClicked[] = {false, false, false, false, false, false, false};
    	private Controler controler;
    	private String name;
    	private boolean animation = false;
     
    	public Panel(Controler controler) {
    		this.setPreferredSize(new Dimension(700, 750));
    		initPanel();
    		this.addMouseListener(this);
    		this.addMouseMotionListener(this);
    		this.controler = controler;
    		this.name = "Panel";
    	}
     
    	public String toString() {
    		return this.name;
    	}
     
    	public void initPanel() {
    		for (int i = 0 ; i < 7 ; i ++) {
    			column[i] = 0;
    			arrowOrigin[i] = new Point2D.Double(origin.getX() + gap + i * (length + gap), origin.getY());
    			arrow[i] = new GeneralPath();
    		}
    		movingPieceOrigin = new Point2D.Double(0, - length - 1);
    	}
     
    	public void paintComponent(Graphics g) {
    		Graphics2D g2d = (Graphics2D)g;
    		g2d.setPaint(new Color(234, 234, 255));
    		g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
    		g2d.setPaint(Color.black);
    		g2d.setStroke(new BasicStroke(1));
    		for (int i = 0 ; i < 7 ; i++) {
    			 arrow[i].moveTo(arrowOrigin[i].getX() + 20, arrowOrigin[i].getY());
    			 arrow[i].lineTo(arrowOrigin[i].getX() + 3 * 20, arrowOrigin[i].getY());
    			 arrow[i].lineTo(arrowOrigin[i].getX() + 3 * 20, arrowOrigin[i].getY() + 3 * 40);
    			 arrow[i].lineTo(arrowOrigin[i].getX() + 80, arrowOrigin[i].getY() + 3 * 40);
    			 arrow[i].lineTo(arrowOrigin[i].getX() + 40, arrowOrigin[i].getY() + 2 * 80);
    			 arrow[i].lineTo(arrowOrigin[i].getX(), arrowOrigin[i].getY() + 3 * 40);
    			 arrow[i].lineTo(arrowOrigin[i].getX() + 20, arrowOrigin[i].getY() + 3 * 40);
    			 arrow[i].closePath();
    			 if (isClicked[i] == true)
    				 g2d.setPaint(Color.yellow);
    			 else if (isMoved[i] == true)
    				 g2d.setPaint(Color.cyan);
    			 else
    				 g2d.setPaint(Color.white);
    			 g2d.fill(arrow[i]);
    			 g2d.setPaint(Color.black);
    			 if (isMoved[i] == true || isClicked[i] == true)
    				 g2d.setStroke(new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
    			 else
    				 g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
    			 g2d.draw(arrow[i]);
    			 g2d.setStroke(new BasicStroke(1));
    		}
    		movingPiece = new Ellipse2D.Double(movingPieceOrigin.getX(), movingPieceOrigin.getY(), 85, 85);
    		if (movingPieceOrigin.getY() > (- 1 ) * 80) {
    			g2d.setPaint(Color.black);
    			g2d.draw(movingPiece);
    			g2d.setPaint(movingPieceColor);
    			g2d.fill(movingPiece);
    			g2d.setPaint(Color.black);
    		}
    	}
     
    	@Override
    	public void update(Color color, int column) {
    		// TODO Auto-generated method stub
    		System.out.println("*** Méthode \"update\" de la classe Panel ***");
    		movePiece(color, column);
    		return;
    	}
     
    	public void movePiece(Color color, int column) {
    		System.out.print("*** Méthode \"movePiece\" de la classe Panel ***");
    		System.out.println(" - animation : " + animation);
    		if (!animation) {
    			System.out.println("\t!animation : " + !animation + " => début animation");
    			animation = true;
    			//this.controler.setAnimation(animation);
    			Runnable runnable = new Runnable() {
    				@Override
    				public void run() {
    					System.out.println("*** Méthode \"run\" du \"Runnable\" du Thread t de la méthode \"movePiece\" de la classe Panel ***");
    					try {
    						int y = -80;
    						movingPieceOrigin.setLocation(origin.getX() + gap / 2 + column * (length + gap), y);
    						movingPieceColor = color;
    						System.out.println("\tDébut de la boucle \"while\" => animation : " + animation);
    						while(animation) {
    							try {
    								Thread.sleep(20);
    								System.out.print(". ");
    							} catch (InterruptedException e) {
    								e.printStackTrace();
    							}
    							y+=10;
    							movingPieceOrigin.setLocation(origin.getX() + gap / 2 + column * (length + gap), y);
    							repaint();
    							if (movingPieceOrigin.getY() >= 750) {
    								System.out.println("");
    								animation = false;
    								controler.setAnimation(animation);
    							}
    						}
    						System.out.println("\tFin de la boucle \"while\"");
    						y = - length;
    					} finally {
    						animation = false;
    						controler.setAnimation(animation);
    					}
    					repaint();
    					System.out.println("\tAprès le dernier repaint");
    				}
    			};
    			Thread t = new Thread(runnable, "movingPiece");
    			t.start();
    		} else {
    			System.out.println("\t!animation : " + !animation + " => pas d'animation");
    		}
    		System.out.print("*** Fin méthode \"movePiece\" de la classe GamePanel ***");
    		System.out.println(" - animation : " + animation);
    		return;
    	}
     
    	public void setMovingPieceOrigin(Point2D origin) {
    		this.movingPieceOrigin.setLocation(origin.getX(), origin.getY());
    	}
     
    	@Override
    	public void mouseClicked(MouseEvent e) {
    		// TODO Auto-generated method stub
    		for (int j = 0 ; j < 7 ; j++) {
    			if (arrow[j].contains(new Point2D.Double(e.getX(), e.getY()))) {
    				//System.out.println("\t\t\tCurseur dans la flèche " + j + " !");
    				isClicked[j] = true;
    				try {
    					this.controler.control(j);
    				} catch (NullPointerException ex) {
    					ex.printStackTrace();
    					System.out.println("\tj = " + j);
    				}
    			} else
    				isClicked[j] = false;
    			this.repaint();
    		}
    	}
     
    	@Override
    	public void mouseEntered(MouseEvent e) {
    		// TODO Auto-generated method stub
    	}
     
    	@Override
    	public void mouseExited(MouseEvent e) {
    		// TODO Auto-generated method stub
    		for (int j = 0 ; j < 7 ; j++) {
    			isClicked[j] = false;
    			isMoved[j] = false;
    			this.repaint();
    		}
    	}
     
    	@Override
    	public void mousePressed(MouseEvent e) {
    		// TODO Auto-generated method stub
     
    	}
     
    	@Override
    	public void mouseReleased(MouseEvent e) {
    		// TODO Auto-generated method stub
    	}
     
    	@Override
    	public void mouseDragged(MouseEvent arg0) {
    		// TODO Auto-generated method stub
    	}
     
    	@Override
    	public void mouseMoved(MouseEvent e) {
    		// TODO Auto-generated method stub
    		boolean arrowChanged = false;
    		for (int j = 0 ; j < 7 ; j++) {
    			if (arrow[j].contains(new Point2D.Double(e.getX(), e.getY()))) {
    				if (!isMoved[j]) {
    					isMoved[j] = true;
    					arrowChanged = true;
    				}
    			} else {
    				if (isMoved[j]) {
    					isMoved[j] = false;
    					arrowChanged = true;
    				}
    			}
    		}
    		if (arrowChanged) {
    			this.repaint();
    			arrowChanged = false;
    		}
    	}
    }
    Ma classe Main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    public class Main {
     
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Color color1 = Color.red;
    		Color color2 = Color.blue;
    		Model model = new Model(color1, color2);
    		model.setCurrentColor(color1);
    		Model tmpModel = new Model(color1, color2);
    		model.setTmpModel(tmpModel);
    		Controler controler = new Controler(model);
    		SwingUtilities.invokeLater(new Runnable() {
    			public void run() {
    				Frame frame = new Frame(model, controler);
    				model.addObserver(frame.getPanel());
    				model.addObserver(frame);
    			}
    		});
    	}
    }
    Et voici ce que j'obtiens en console lorsque je lance le programme et que je clique sur l'une des flèches :
    *** Méthode "move" de la classe Model ***
    Début du Thread "moveThread"
    *** Méthode "notifyObserver" de la classe Model ***
    *** Méthode "update" de la classe Panel ***
    *** Méthode "movePiece" de la classe Panel *** - animation : false
    !animation : true => début animation
    *** Fin méthode "movePiece" de la classe GamePanel *** - animation : true
    Fin du Thread "moveThread"
    animation : false => Pas de boucle d'attente
    Appel à la méthode chooseNextMove
    *** Méthode "chooseNextMove" de la classe Model ***

    *** Méthode "move" de la classe Model ***
    Début du Thread "moveThread"
    *** Méthode "notifyObserver" de la classe Model ***
    *** Méthode "update" de la classe Panel ***
    *** Méthode "movePiece" de la classe Panel *** - animation : true
    !animation : false => pas d'animation
    *** Fin méthode "movePiece" de la classe GamePanel *** - animation : true
    Fin du Thread "moveThread"
    animation : false => Pas de boucle d'attente
    *** Méthode "run" du "Runnable" du Thread t de la méthode "movePiece" de la classe Panel ***
    Début de la boucle "while" => animation : true
    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
    *** Méthode "setAnimation" de la classe Controler *** = > animation : false
    *** Méthode "setAnimation" de la classe Model *** = > animation : false
    Fin de la boucle "while"
    *** Méthode "setAnimation" de la classe Controler *** = > animation : false
    *** Méthode "setAnimation" de la classe Model *** = > animation : false
    Après le dernier repaint
    Donc, de ce que je comprends :
    • Lorsque que je clique sur l'une des flèches, la méthode "mouseClicked" du Panel appelle la méthode "control" du Controler.
    • Cette méthode appelle la méthode "move" du Model.
    • La méthode "move" appelle la méthode "notifyObserver", toujours dans le Model.
    • Cette dernière appelle la méthode "update" du Panel (la vue).
    • La méthode "update" appelle la méthode "movePiece" du Panel ; cette méthode est là pour gérer l'animation avec le Thread t, mais celui-ci ne se lance pas tout de suite ! A mon avis, c'est à partir de là que
    • Au lieu de cela, la méthode "move" du Model continue et appelle la méthode "chooseNextMove", sans passer par la boucle d'attente car le booléen "animation" est "false".
    • La méthode "chooseNextMove" rappelle la méthode "move" du Modelel.
    • Cela relance la suite d'appel aux méthodes "notifyObserver" (Model), "update" et "movePiece" (Panel), comme précédemment.
    • C'est à ce moment-là seulement que le Thread t est démarré et sa méthode "run" éxécutée.
    • La 1ère animation a bien lieu : un disque rouge descend du haut en bas du Panel ; pendant ce temps la console renvoie les ". . . . ".
    • Puis, la méthode "setAnimation" du Controler est appelée pour passer le booléen "animation" à false.
    • Idem pour la méthode "setAnimation" du Model.
    • Ces méthodes sont appelées une 2e fois ; j'imagine que c'est suite au 2e appel à la méthode "movePiece", mais cette dernière n'a jamais lancé la 2e animation.
    • Puis, fin du programme.


    Donc, comment faire pour que la 1ère animation se lance bien ? Puis pour que la 2e se lance après que la programme l'a décidé ?
    En gros, j'aimerais que la 1ère animation se lance et se finisse AVANT que la méthode "chooseNextMove" du Model ne soit appelée. Ainsi, lorsque cette dernière serait appelée, elle reprendrait puis la 2e animation se lancerait.
    Comment marquer une pause et attendre la fin de l'animation avant de reprendre l'éxécution de la méthode ?

    J'essayé de mettre des Threads à différents endroits, mais sans succès. J'ai l'impression que mon problème vient de différents threads lancés par les méthodes appelées entre elles.

    Faut-il utiliser des Threads avec les méthodes "wait" et "notify" ? et si oui, comment ?
    Ou la classe SwingWorker ? mais je n'ai pas compris comment elle fonctionne et si elle peut être utile ici.
    Ou autre chose (j'ai lu des choses sur Executor ou des Semaphores sans bien comprendre comment l'appliquer à mon problème) ?

    Je précise que je suis débutant en Java. J'espère avoir été clair sur mon problème. J'ai tout extrait d'un code plus long et plus complexe en le simplifiant au max pour l'exemple, il est possible que j'ai laissé des bouts de code incohérents ou inutiles. Je n'ai pas mis les classes abstraites ni les interfaces Observer et Observable pour ne pas surcharger, mais je les rajoute si besoin.

    Merci d'avoir lu jusqu'ici !

  2. #2
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Hello je te conseille de lire ce post pour tenter de comprendre un peu mieux comment l'UI gère son affichage et ses évènements. Dans ce post il est question du JavaFX Application Thread de JavaFX mais le concept est exactement le même pour l'Event Dispatch Thread de AWT/Swing/Java2D.

    Donc oui, quand tu traites un évènement, il y a aucun rafraîchissement de l'affichage.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  3. #3
    Membre à l'essai
    Homme Profil pro
    Ingénieur process
    Inscrit en
    Mars 2017
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur process
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2017
    Messages : 17
    Points : 17
    Points
    17
    Par défaut
    Salut Bouye et merci beaucoup pour ton retour.

    J'ai bien relu le post que tu m'as conseillé, ainsi que d'autres ou des tutos sur les Thread, notamment en Swing. J'ai cru comprendre que l'équivalent de Platform.runLater() en Swing est SwingUtilities.invokeLater(), et j'ai essayé d'adapter ça à mon code.

    J'ai d'abord mis tout le code de la méthode "move" de la classe "Model" dans un Thread à part pour qu'il ne soit plus exécuté dans l'EDT (Thread auquel j'ai affecté une minPriority comme dans ton exemple de l'autre post). Puis j'ai mis lancé la boucle de mise à jour de l'animation de la méthode "movePiece" de la classe "Panel" dans l'EDT avec SwingUtilities.invokeLater(), mais ça n'a pas fonctionné : je n'avais même plus la 1ère animation me semble-t-il, donc aucune mise à jour de l'affichage.
    J'ai changé le SwingUtilities.invokeLater() par SwingUtilities.invokeAndWait() (avec les try / catch adaptés) pour bloquer le Thread courant pendant la mise à jour, mais ça n'a pas fonctionné mieux.
    Par contre les traces dans la console me disaient bien que le code s'exécutait comme je le voulais, c'est-à-dire que la méthode "movePiece" était fini avant l'appel à la méthode "choosNextMove" de la classe "Model". C'était déjà une grosse avancée.
    Puis, j'ai modifié le code de la méthode "movePiece" dans la classe "Panel" pour ne laisser dans le Runnable que l'appel à la méthode "repaint()" et non plus tout le reste, et notamment le Thread.sleep(), cf. ce que tu disais :
    Citation Envoyé par bouye Voir le message
    Tu bloque le thread événementiel du toolkit UI (Event Dispatch Thread dans AWT/Swing, JavaFX application thread ici) par un sleep() - il ne faut JAMAIS faire cela.
    .

    Et là, ça fonctionne plutôt bien ! Je dis "plutôt bien" car les 2 animations s'enchaînent bien à la suite l'une de l'autre comme je le souhaite, mais la 2e animation est un peu plus rapide que la 1ère, et je ne comprends pas pourquoi. J'ai mis un compteur dans la méthode movePiece pour traquer ça, et j'ai effectivement une différence de 0,5 à 0,8 seconde environ, soit de 15 à 25 % de temps en moins !

    Quelqu'un saurait-il me dire pourquoi c'est plus rapide au 2e appel ? et ce qu'il faudrait faire pour éviter ce phénomène ?

    Je remets mon code modifié pour plus de clareté.

    Classe Model, méthode "move" uniquement, les autres n'ont pas changé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     
    public class Model extends AbstractModel {
    	//Tout le reste n'a pas changé
     
    	@Override
    	public synchronized void move(Color color, int column, boolean mainModel) {
    		// TODO Auto-generated method stub
    		System.out.println("\n*** Méthode \"move\" de la classe Model ***");
    		Thread t = new Thread(new Runnable() {
    			public void run() {
    				notifyObserver(color, column);
     
    				if (mainModel) {
    					currentColor = (currentColor == color1) ? color2 : color1;
    					if (currentColor == color2) {
    						System.out.println("\tAppel à la méthode chooseNextMove");
    						chooseNextMove();
    					}
    				}
    			}
    		}, "moveThread");
    		t.setPriority(Thread.MIN_PRIORITY);
    		t.start();
    	}
     
    	public void chooseNextMove() {
    		System.out.println("*** Méthode \"chooseNextMove\" de la classe Model ***");
    		this.move(currentColor, 3, true);
    	}
    }
    Classe Panel, méthode "movePiece" uniquement, les autres n'ont pas changé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
     
    public class Panel extends JPanel implements Observer, MouseListener, MouseMotionListener {
    	//Tout le reste n'as pas changé...
     
    	public synchronized void movePiece(Color color, int column) {
    		System.out.print("*** Méthode \"movePiece\" de la classe Panel ***");
    		System.out.println(" - animation : " + animation);
    		long startTime = System.currentTimeMillis();
     
    		if (!animation) {
    			System.out.println("\t!animation : " + !animation + " => début animation");
    			animation = true;
    			Runnable runnable = new Runnable() {
    				@Override
    				public void run() {
    					repaint();
    				}
    			};
    			try {
    				int y = -80;
    				movingPieceOrigin.setLocation(origin.getX() + gap / 2 + column * (length + gap), y);
    				movingPieceColor = color;
    				System.out.println("\tDébut de la boucle \"while\" => animation : " + animation);
    				while(animation) {
    					try {
    						Thread.sleep(40);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					y+=10;
    					movingPieceOrigin.setLocation(origin.getX() + gap / 2 + column * (length + gap), y);
    					SwingUtilities.invokeLater(runnable);
    					if (movingPieceOrigin.getY() >= 750) {
    						System.out.println("");
    						animation = false;
    					}
    				}
    				System.out.println("\tFin de la boucle \"while\"");
    				y = - length;
    			} finally {
    				animation = false;
    			}
    			SwingUtilities.invokeLater(runnable);
    			System.out.println("\tAprès le dernier repaint");
    		} else {
    			System.out.println("\t!animation : " + !animation + " => pas d'animation");
    		}
    		System.out.print("*** Fin méthode \"movePiece\" de la classe Panel ***");
    		System.out.println(" - animation : " + animation);
    		System.out.println("\tDurée de l'animation : " + (System.currentTimeMillis() - startTime));
    		return;
    	}
     
    	// Tout le reste n'as pas changé...
    }
    Voici ce que j'obtiens en console :
    *** Méthode "move" de la classe Model ***
    *** Méthode "notifyObserver" de la classe Model ***
    *** Méthode "update" de la classe Panel ***
    *** Méthode "movePiece" de la classe Panel *** - animation : false
    !animation : true => début animation
    Début de la boucle "while" => animation : true

    Fin de la boucle "while"
    Après le dernier repaint
    *** Fin méthode "movePiece" de la classe Panel *** - animation : false
    Durée de l'animation : 4126
    Appel à la méthode chooseNextMove
    *** Méthode "chooseNextMove" de la classe Model ***

    *** Méthode "move" de la classe Model ***
    *** Méthode "notifyObserver" de la classe Model ***
    *** Méthode "update" de la classe Panel ***
    *** Méthode "movePiece" de la classe Panel *** - animation : false
    !animation : true => début animation
    Début de la boucle "while" => animation : true

    Fin de la boucle "while"
    Après le dernier repaint
    *** Fin méthode "movePiece" de la classe Panel *** - animation : false
    Durée de l'animation : 3590
    Encore un grand merci pour ton aide !

  4. #4
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Bien c'est un très bon début. SwintUtilities.invokeLater() est bien l'équivalent Swing de Platform.runLater() de JavaFX.

    Le soucis vient du fait qu'en faisant ainsi tu ne contrôle pas vraiment le framerate (le taux de rafraichissement de l'animation). En effet ce que tu veux faire c'est une animation à framerate constant, c'est à dire se rapprochant de ce qu'on peut trouver dans le dessin animé (2D ou CGI), les films ou encore le jeu vidéo. Ce qui demande un contrôle bien plus fin du moment où le rafraichissement se fait et de l'étape de l'animation à dessiner. C'est ce qu'on appelle la boucle principale dans les jeux. Et ça s'applique dans une moindre mesure aussi aux animations de l'UI.

    On peut faire ça avec un thread qui boucle à 1/60 fps (par exemple), modifie l'état de l'animation et invoque repaint() à la fin de son traitement. Cependant, les deux framework proposent aussi des classes pour les traitements de tache de longue durée qui se répètent sans devoir coder les threads à la main : en JavaFX c'est ScheduledService + Task (mais on peut aussi coder avec l'AnimationTimer qui est entièrement dédié à cela) et en Swing c'est javax.swing.Timer (différent de java.util.Timer). Tu peux donc commencer à faire des tests d'animation avec cette classe Timer pour par exemple dessiner une forme géométrique qui bouge à l'écran

    Astuce : dans ton timer tu positionnes les coordonnées de la forme en fonction du temps actuel sachant que tu connais les coordonnées de son point de départ ainsi que celle de son son point d'arrivée. Et, lorsque tu fais repaint(), ton composant va prendre cette position et dessiner la forme géométrique à la bonne position dans sa méthode repaintComponent().


    Il y a ensuite des optimisations pour rendre les animations plus fluides mais avant de procéder vérifier que tout fonctionne via une animation simple.

    Je te conseille ensuite de te pencher sur ces pratiques telles qu'elles sont discutées ici, car il existe des bibliothèques tierces qui peuvent te faciliter la vie dans la création de ton animation.

    Et avant que tu ne poses la question, oui faire des animations en Swing c'est compliqué (mais pas impossible) car la bibliothèque a été conçue à la fin des années 90, à une époque ou les GUI étaient bien plus statiques que maintenant.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  5. #5
    Membre à l'essai
    Homme Profil pro
    Ingénieur process
    Inscrit en
    Mars 2017
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur process
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2017
    Messages : 17
    Points : 17
    Points
    17
    Par défaut
    Bonjour Bouye.

    Je te remercie pour ce nouveau retour (et désolé d'avoir moi-même répondu si tard).
    J'ai regardé la doc de javax.swing.timer ainsi que les liens que tu proposes. Effectivement, ça va pouvoir m'aider pour ce que je cherche à faire. Je n'ai pas eu le temps de faire des tests pour l'instant, mais je le ferai dès que possible, et je reviendrai sur ce sujet si je galère avec le timer, ou j'ouvrirai un nouveau sujet au besoin.
    En attendant, je passe le sujet en "Résolu" car le problème initial l'est bien, grâce à toi. Encore merci.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 03/04/2019, 15h18
  2. [PowerShell] Attendre la fin d'une exécution pour passer à la suivante
    Par AntoLili dans le forum Scripts/Batch
    Réponses: 0
    Dernier message: 18/09/2018, 09h58
  3. [AC-2007] attendre la fin d'une etape pour passé a l autre étape
    Par popofpopof dans le forum VBA Access
    Réponses: 11
    Dernier message: 01/06/2018, 21h34
  4. [PHP 5.0] Attendre la fin d'une fonction pour en realiser une autre
    Par keaton7 dans le forum Langage
    Réponses: 2
    Dernier message: 07/05/2009, 16h06
  5. [FLASH MX2004] Attendre la fin d'une anim
    Par stailer dans le forum Flash
    Réponses: 2
    Dernier message: 11/07/2005, 12h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo