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 :

Bouton + thread


Sujet :

EDT/SwingWorker Java

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut Bouton + thread
    Bonjour!
    J'ai malheureusement pris un cours de deuxième année et me voilà un peu dans le trouble. Même si je connais niveau débutant l'orienté objet, je n'ai pas eu l'occasion de suivre un cours sur les Threads. J'ai cherché sur le net et il me semble avoir saisi le principe. Toutefois, on m'a demandé d'implémenter un régulateur PID pour qu'une voiture atteigne une vitesse donnée. Mon problème est qu'ils nous on fournit un .jar avec la classe (je l'ai décompilé) CarSimulator qui implémente Runnable. Comme suit :
    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
    package CarSimulator;
     
    import java.util.Random;
     
    public class CarSimulator implements Runnable {
        Random randomGenerator = new Random();
        double airCoefficient = 0.001D;
        double gravitationAcceleration = 9.81D;
        double currentSlope = 0.0D;
        final double maxSlopeChange = 0.5D;
        double speed = 0.0D;
        double acceleration = 0.0D;
        double totalAcceleration = 0.0D;
        boolean stop = false;
        long oldTime = System.currentTimeMillis();
        long measuredTime = System.currentTimeMillis();
        long time = System.currentTimeMillis();
     
        public CarSimulator() {
        }
     
        public void run() {
            while(!this.stop) {
                this.totalAcceleration = this.acceleration - Math.signum(this.speed) * this.airCoefficient * this.speed * this.speed - this.gravitationAcceleration * Math.sin(Math.atan(this.currentSlope / 100.0D));
                this.time = System.currentTimeMillis();
                this.speed += this.totalAcceleration * (double)(this.time - this.oldTime) / 1000.0D;
                this.oldTime = this.time;
                if (System.currentTimeMillis() - this.measuredTime > 1000L) {
                    this.currentSlope = this.currentSlope + this.randomGenerator.nextDouble() * 2.0D - 1.0D;
                    if (this.currentSlope > 15.0D) {
                        this.currentSlope = 15.0D;
                    } else if (this.currentSlope < -15.0D) {
                        this.currentSlope = -15.0D;
                    }
     
                    this.measuredTime = System.currentTimeMillis();
                }
            }
     
        }
     
        public void stop() {
            this.stop = true;
        }
     
        public void setAcceleration(double paramDouble) {
            this.acceleration = paramDouble;
        }
     
        public double getSpeed() {
            return this.speed;
        }
    }
    Du coup moi j'ai implémenté ma classe "static" PID et tout et tout. MAIS ils nous ont aussi demandé de faire un small GUI que j'ai fait comme suit:
    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
    import CarSimulator.CarSimulator;
     
    import javax.swing.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    public class CruiseControl {
        private JButton startSimulButton;
        private JPanel panel;
     
        private CarSimulator car = new CarSimulator();
        Thread simul = new Thread(car,"tutu");
     
        public CruiseControl() {
     
     
     
            startSimulButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    PID.set(1,1,0);
                    PID.setTarget(30);
     
                    simul.start();
     
                    while (true){
                        car.setAcceleration(PID.calculate(car.getSpeed(),0));
                        System.out.println(car.getSpeed());
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }
                    }
                }
            });
        }
     
     
        public static void main(String[] args) {
     
            JFrame frame = new JFrame();
            frame.setContentPane(new CruiseControl().panel);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
     
    }
    Le souci comme vous le verrez, c'est que quand j'appuie sur le bouton start simul, le programme fait bien ce que je veux. Cependant il n'y a aucun moyen de sortir de là! Il y a clairement quelque-chose que je ne saisit pas avec les threads car je ne vois pas comment injecter une vitesse non-stop sans passer par une boucle du style. Notez que je vous demande pas de me faire l'exo, car autant les threads comme le gui ne sont pas sur quoi porte le cours mais des notions que mes collègues ont déjà vues. Le truc important c'est le PID. Mais voilà c'est embêtant.
    J’espère que vous pourrez me donner qques indications, merci d'avance

  2. #2
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Ton problème est la mauvaise gestion de l'EDT (Event Dispatch Thread).
    Il y a tout ce qu'il faut dans la FAQ Java du site.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  3. #3
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut
    Citation Envoyé par dinobogan Voir le message
    Ton problème est la mauvaise gestion de l'EDT (Event Dispatch Thread).
    Il y a tout ce qu'il faut dans la FAQ Java du site.
    Effectivement ça m'a l'air d'être ça. Mais j'ai du mal à résoudre le problème car CarSimulator est déjà un Runnable.
    Pensez-vous que la solution est de ce style? :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void actionPerformed(ActionEvent e) {
      new Thread(new Runnable() {
          public void run() {
            final String text = readHugeFile();
            SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                textArea.setText(text);
              }
            }
          }
      }).start();
    }
    J'ai vu ça dans : https://gfx.developpez.com/tutoriel/...ing-threading/

  4. #4
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    Le principal problème est ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     while (true){
                        car.setAcceleration(PID.calculate(car.getSpeed(),0));
                        System.out.println(car.getSpeed());
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }
                    }
    Tu fais une boucle infinie dans l'Event Dispatch Thread : tu le prends pour toi, il ne fait plus que ce qui est dans cette boucle et tout le reste qu'il est censé faire (gérer l'affichage, l'interaction, etc), il ne peut plus.

    Au lieu d'exécuter cette boucle dans l'Event Dispatch Thread, elle devrait s'exécuter dans un thread à part, et si, dans ce thread, tu as besoin de mettre à jour l'UI (changer un texte dans un JLabel par exemple), il faut déléguer à l'Event Dispatch Thread (par SwingUtilities.invokeLater, comme indiqué dans le dernier code que tu montres).
    Il y a une classe qui aide à implémenter ce genre de mécanismes : SwingWorker. Le principe est qu'il y a une méthode pour faire la boucle (elle s'exécutera dans un thread à part) : dans cette boucle tu génères des "modifications d'ui", qu'une autre méthode s'exécutant dans l'Event Distpatch Thread va "recevoir", ce qui te permettre de mettre à jour l'UI.

    Enfin, il faut tenir compter que CarSimulator est un runnable exécuté par un troisième thread (1: Event Dispatch Thread, 2:Swing Worker thread, 3:CarSimulator thread). Il faut en tenir compte par rapport à la concurrence d'accès (la boucle qui modifie l'accélération est dans un thread, alors que la boucle de la voiture qui l'utilise est dans un autre : concurrence d'accès entre deux threads). Il faut donc par exemple s'arranger pour que lorsqu'un thread exploite un état, l'autre ne puisse par le modifier, et inversement. Avec synchronized par exemple.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  5. #5
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut
    Merci beaucoup pour votre aide!
    il me semble que j'ai résolu le problème et j'ai à peu près compris!
    Si ce n'est pas trop vous demander, pourriez-vous de jeter un bref coup d’œil voir si je n'ai pas fais d'aberrations en tout cas le programme fonctionne.
    Merci encore !

    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
    import CarSimulator.CarSimulator;
     
    import javax.swing.*;
     
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    public class MainFrame {
     
     
        private boolean simul=false;
        private JPanel panel1;
        private JButton stopButton;
        private JButton startButton;
     
     
        public MainFrame(String title){
     
    JFrame frame = new JFrame();
    frame.setSize(500,500);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(panel1);
    frame.setVisible(true);
     
            stopButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    simul=false;
                }
            });
            startButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    simul=true;
                    start();
                }
            });
        }
        private void start(){
            SwingWorker<Void,Void> worker = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    CarSimulator car = new CarSimulator();
                    PID.set(1,1,0);
                    PID.setTarget(30);
                    Thread c =new Thread(car);
                    c.start();
                    while (simul){
     
                        car.setAcceleration(PID.calculate(car.getSpeed(),0));
                        System.out.println(car.getSpeed());
                        Thread.sleep(500);
                    }
     
                    return null;
                }
            };
            worker.execute();
        }
    }

  6. #6
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    1. la variable simul est écrite par un thread (Event Dispatch Thread) et lue par un autre (celui du SwingWorker). Ceci peut poser des problèmes en particulier sur des machnes multi core, car la valeur d'une variable est "copiée" pour chaque thread. quand l'un modifie la valeur, l'autre n'est pas forcément immédiatement au courant. Pour éviter ce genre de problème, on peut déclarer la variable volatile. C'est d'ailleurs également le cas pour la variable stop de CarSimulator.
      Pour simul, une autre solution serait d'utiliser la méthode cancel() de SwingWorker pour gérer l'arrêt du SwingWorker.

      1. au lieu de la variable simul, une variable SwingWorker worker
      2. pour le bouton stop:
        Code : Sélectionner tout - Visualiser dans une fenêtre à part
        1
        2
        3
        4
                   	if(worker!=null) {
                    		worker.cancel(true);
                    		worker=null;
                    	}
      3. pour la méthode start :
        Code : Sélectionner tout - Visualiser dans une fenêtre à part
        worker=new SwingWorker<Void, Void>() {...
    2. pour la boucle dans doInBackground() :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
                     while (!isCancelled()){
       
                          car.setAcceleration(PID.calculate(car.getSpeed(),0));
                          System.out.println(car.getSpeed());
                          try {
                          Thread.sleep(500);
                          }
                          catch(InterruptedException e) {
                          	break;
                          }
                      }
    3. les boutons sont actionnables n'importe comment : on peut arrêter avant de démarrer, démarrer 2 fois de suite, etc...
      Si arrêter avant de démarrer n'a aucune conséquence, démarrer 2 fois va lancer 2 SwingWorker. Cela peut être voulue pour pouvoir lancer plusieurs voitures en même temps. En revanche, le bouton stop va arrêter toutes les voitures démarrées précédemment. Avec la méthode que je t'ai donnée au point précédent, en revanche, ça pose un gros problème : seul le dernier SwingWorker sera arrêter.
      1. soit on veut gérer plusiers voitures en même temps, et il faut une liste de SwingWorker et possiblement d'autres adaption, en particulier sur l'UI
      2. soit on empêche d'avoir plusieurs "swingworker"
        1. on peut ajouter dans start
          Code : Sélectionner tout - Visualiser dans une fenêtre à part
          1
          2
          3
          4
          if ( worker==null )  {
              worker = new SwingWorker()...
              worker.execute();
          }
        2. on peut gérer ça de manière plus clean au niveau UI en utilisant l'état enabled des boutons
          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
           
                  stopButon.setEnabled(false);
                  stopButton.addActionListener(new ActionListener() {
                      @Override
                      public void actionPerformed(ActionEvent e) {
                          simul=false;
                          stopButon.setEnabled(false);
                          startButon.setEnabled(true);
                      }
                  });
                  startButton.addActionListener(new ActionListener() {
                      @Override
                      public void actionPerformed(ActionEvent e) {
                          simul=true;
                          start();
                          startButon.setEnabled(false);
                          stopButon.setEnabled(true);
                      }
                  });
        3. Eventuellement, le toggle button :
          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
                 	toggleButton = new JToggleButton("Start");
                 	toggleButton.addActionListener(new ActionListener() {
           
          			@Override
          			public void actionPerformed(ActionEvent e) {
          				if( toggleButton.isSelected() ) {
          	                simul=true;
          	                start();
          	                toggleButton.setText("Running...");
          				}
          				else {
          					simul=false;
          	                toggleButton.setText("Start");
          				}
          			}
          		});
          mais le mieux probablement serait un bouton poussoir, qui se relève quand on lache le bouton de la souris, mais ça n'existe pas en standard, mais c'est faisable
      3. il y a un souci avec CarSimulator : à chaque nouveau SwingWorker, on en créer une nouvelle instance et on lance un thread. Du coup, on a accumulation en mémoire d'instance de CarSimulator, sur lesquelles on n'a plus aucune référence permettant de les arrêter.
        Cela peut être voulu de ne pas arrêter le thread de CarSimulateur à la fin de doInBackground() du SwingWorker, mais c'est à gérer d'une manière ou d'une autre.


    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

Discussions similaires

  1. probleme thread sur bouton
    Par Greesize dans le forum Windows Forms
    Réponses: 15
    Dernier message: 22/09/2007, 19h21
  2. [Thread] Déplacement bouton
    Par Madfrix dans le forum AWT/Swing
    Réponses: 2
    Dernier message: 14/08/2007, 11h09
  3. Réponses: 3
    Dernier message: 16/03/2007, 11h36
  4. Gestion d'un thread aux boutons
    Par arsenik7 dans le forum AWT/Swing
    Réponses: 4
    Dernier message: 02/06/2006, 12h01
  5. Réponses: 7
    Dernier message: 03/05/2006, 16h13

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