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 :

[Thread] Fenêtre d'attente


Sujet :

EDT/SwingWorker Java

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 19
    Par défaut [Thread] Fenêtre d'attente
    Salut à tous,

    Je vous explique le contexte.
    J'ai une application qui exploite des fichiers binaire contenant de nombreuses données. Les fichiers peuvent être assez gros et l'ouverture peut demander du temps (jusqu'à une minute mais cela pourrait être plus long).

    L'ouverture d'un fichier est découpée en morceau, lecture, statistiques, etc...
    Les étapes se suivent et sont indissociables.
    Tout se fait automatiquement.

    L'étape la plus longue et la lecture du fichier. C'est pour cette étape que je voudrais mettre une fenêtre d'attente.

    J'ai tenté de mettre en application le bon exemple de la FAQ sur les fenêtres d'attente mais sans succès.

    Alors j'ai crée une classe héritant d'une JDialog et recevant en argument un objet de type JAbstractTask qui est en gros un Thread.

    Le dialog s'initialise bien et lance le Thread mais ma fenêtre n'apparait que lorsque le Thread est fini donc retour à la cas départ !

    Comment faire pour que mon interface vive et qu'en même temps la tache d'ouverture soit bloquante !

    Merci pour vos conseil.
    A+

  2. #2
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2007
    Messages
    697
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2007
    Messages : 697
    Par défaut
    une piste:la classe SwingUtilities et sa méthode
    invokeAndWait(Runnable r) qui arrête le thread principal et force le dessin.
    Si ça peut t'aider...

  3. #3
    Expert confirmé
    Avatar de le y@m's
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2005
    Messages
    2 636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Février 2005
    Messages : 2 636
    Je ne répondrai à aucune question technique par MP.

    Pensez aux Tutoriels et aux FAQs avant de poster ;) (pour le java il y a aussi JavaSearch), n'oubliez pas non plus la fonction Rechercher.
    Enfin, quand une solution a été trouvée à votre problème
    pensez au tag :resolu:

    Cours Dvp : http://ydisanto.developpez.com
    Blog : http://yann-disanto.blogspot.com/
    Page perso : http://yann-disanto.fr

  4. #4
    Membre éclairé Avatar de rems033
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    513
    Détails du profil
    Informations personnelles :
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2007
    Messages : 513
    Par défaut
    SwingUtilities est la solution.
    J'avais le meme probleme aujourd'hui résolu
    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
     
    new Thread(new Runnable() {
                        public void run() {
     
                            final FenetreInstant t = new FenetreInstant("Extraction en cours...") ;
     
                            //On modifie l'état  d'un composant Swing => Opération à effectuer dans l'EDT
                            SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                    t.setVisible(true);
                                }
                            });
     
                            //ta tache de fond n'a rien a voir avec l'EDT
                            tache_de_fond();
     
                            SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                    t.dispose();
                                }
                            });
     
     
                        }
                    }).start() ;
    la fenetre t représente ta fenetre pour faire patienter...il faut l'initialiser avec setVisible(false).
    Si ca peux t'aider....
    Bon courage!

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 19
    Par défaut
    Salut,

    Merci pour vos réponse.

    une piste:la classe SwingUtilities et sa méthode
    invokeAndWait(Runnable r) qui arrête le thread principal et force le dessin.
    Si ça peut t'aider...
    Tenté mais j'ai un beau :
    Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread

    Voilà trois tutoriels qui me semblent pouvoir t'apporter une solution
    Je vais regarder car visiblement certaines choses m'échappent !!!

    rems033, merci pour ton code, je vais essayer mais je voulais faire quelque chose de très réutilisable et modifiant le moins possible mon code existant.

    Une question quand même, pourquoi n'y a t'il pas de méthode générique ? est-ce un manque du langage ou est-ce trop dure de faire du code réutilisable ?
    A+

  6. #6
    Membre Expert
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Par défaut
    Citation Envoyé par carlierd
    Une question quand même, pourquoi n'y a t'il pas de méthode générique ? est-ce un manque du langage ou est-ce trop dure de faire du code réutilisable ?
    A+
    C'est à dire que rien ne stipule à l'avance le temps que va prendre ton traitement.

    Pour qu'une GUI soit réactive il faut mettre les traitements longs dans un thread à part, de façon à libérer le plus rapidement possible l'interface utilisateur.

    Comme cette opération peut avoir des conséquences lourdes, c'est au programmeur d'apprécier si elle est adéquate, et quelle est la meilleure façon de la mettre en oeuvre. (pour qu'elle n'ait pas de conséquences lourdes, évidemment).

  7. #7
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2007
    Messages
    697
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2007
    Messages : 697
    Par défaut
    Citation Envoyé par carlierd
    :
    Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
    en faite danq une appli tu as le thread principal qui demarre avec la methode main et le event dispatcher thread(EDT) qui modifie les composant swing qui démmarre en parralelle. La methode invokeAndWait demande au EDT de faire une modification de l'interface. Mais surtout demande de le faire en bloquant le main thread jusqu'a que l'EDT est fini sa tache.
    Fait voir comment tu la utilisé je pens qu'il y a un problème, tu n'est pas dans le bon thread

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 19
    Par défaut
    Salut,

    En faite, ma fenêtre principale lance un JDialog qui est bloquant. C'est ce JDialog qui doit lancer la tache longue, de cette façon:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public static void showDialog(Component comp)
        {
        //Si le dialogue est crée, on l'affiche en le centrant par rapport au comp
        if (dialog != null) {dialog.setLocationRelativeTo(comp); dialog.setVisible(true);}
        else {System.err.println("This dialog requires you to call initialize before !!");}
     
        dialog.startProcessing();
        }
    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
    public void startProcessing()
        {
        // Activation du Thread
        task.start();
        // tant que le thread est en vie...
        while( task.isAlive() )
          {
          // faire un traitement...
          progressBar.setValue(progressBar.getValue() + 1);
          System.out.println("Ligne affichée par le main");
          try
            {
            // et faire une pause
            Thread.sleep(800);
            }
          catch (InterruptedException ex) {}
          }
        }
    Le code de la tache (pour test):
    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
    public class JAbstractTask extends Thread
      {
      public void run()
        {
        long start = System.currentTimeMillis();
     
        // boucle tant que la durée de vie du Thread est < à 5 secondes
        while( System.currentTimeMillis() < ( start + (1000 * 5)))
          {
          // traitement
          System.out.println("Ligne affichée par le thread");
          try
            {
            // pause
            Thread.sleep(500);
            }
          catch (InterruptedException ex) {}
          }
        }
      }
    Le truc c'est que dialog.setVisible(True) ne rend la main que lorsque la fenêtre est fermée. Si je lance la tache lors de la création du JDialog, l'affichage est bloqué et ma fenêtre n'apparait que lorsque le traitement est bloqué.

    Comment je pourrais faire ?
    Dans le cas présent, il y a 3 threads, le main qui serait le JDialog, l'EDT et mon thread JAbstractTask ?
    Il faudrait donc que l'EDT puisse avoir la main pour rafraichir l'interface, je pensais que c'est ce que je faisais avec la boucle while et le Thread.sleep dans le JDialog !
    A+

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 19
    Par défaut
    Salut gifffftane,

    C'est à dire que rien ne stipule à l'avance le temps que va prendre ton traitement.

    Pour qu'une GUI soit réactive il faut mettre les traitements longs dans un thread à part, de façon à libérer le plus rapidement possible l'interface utilisateur.

    Comme cette opération peut avoir des conséquences lourdes, c'est au programmeur d'apprécier si elle est adéquate, et quelle est la meilleure façon de la mettre en oeuvre. (pour qu'elle n'ait pas de conséquences lourdes, évidemment).
    En faite dans certain cas, je veux bien le comprendre mais il pourrait y avoir un cas général comme par exemple ce que j'essaye de faire, un dialog qui se lance et qui lance une tache perso. Moi cela répondrait à pas mal de besoin soit bloquer l'interface principale (le JDialog) et lancer ma tache longue tout en affichant la progression.
    A+

  10. #10
    Membre éprouvé
    Avatar de Janitrix
    Inscrit en
    Octobre 2005
    Messages
    3 391
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 3 391
    Par défaut
    Et pourquoi pas utiliser le GlassPane de ta fenêtre ? Personnellement, je fais comme ça et ça marche très bien. Tu étends la classe JPanel et fais un rectangle de couleur plus ou moins transparente. Tu affiches au centre de ce panel un texte genre "Chargement, merci de patienter", tu n'oublies pas de lui ajouter un MouseListener pour bloquer les éventuelles cliques sur ta fenêtre principale, et tu mets ce composant en GlassPane. C'est simple, propre et efficace. Tu peux même t'amuser à faire des animations (bouger le texte, changer la transparence des couleurs...).

    Si tu veux un screeshot, c'est quand tu veux

    Bonne chance.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 19
    Par défaut
    Salut,

    C'est effectivement une autre solution vers laquelle je m'orienterais peut-être mais je veux comprendre ce qui ne fonctionne pas dans mon code avant.

    Je ne vois pas pourquoi le thread EDT est bloqué sachant que le thread main fait des sleep et que le thread que je crée ne fait que des sleep !

    Des idées ?
    A+

  12. #12
    Membre Expert
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Par défaut
    Autant que je puisse comprendre les choses :

    1. à partir de je ne sais quoi dans ta GUI, donc, techniquement, dans le dispatching thread, tu inities d'une part un JDialog d'attente, d'autre part un traitement long.
    2. pour le traitement long, il faut lancer un thread, séparé du dispatching thread ; IL NE FAUT PAS - je crois qu'il y a une erreur de certains d'entre nous là dessus - lancer ce thread par le biais de SwingUtilities, puisque l'on ne veut pas que ce thread soit dans le dispatching thread GUI, sous peine de tout rebloquer.

    Pense bête :
    • traitement GUI Dispatching Thread SwingUtilities.
    • traitement hors GUI thread normal.

  13. #13
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2007
    Messages
    697
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2007
    Messages : 697
    Par défaut
    dans ton code où lances tu le calcul ? Il me semble tu as zappé cette partie?

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 19
    Par défaut
    Salut à tous,

    Comme je suis pas super clair, je vais essayer de faire un peu mieux !!

    Donc dans ma JFrame, sur action d'un bouton je lance le traitement suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    JWaitWindow.initialize(this, new JAbstractTask());
    JWaitWindow.showDialog(null);
    Voici en gros la classe JWaitWindow :

    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
    public static void initialize(Component comp, JAbstractTask task)
        {
        //Create frame and dialog
        Frame frame = JOptionPane.getFrameForComponent(comp);
        dialog = new JWaitWindow(frame, task);
        }
     
      public static void showDialog(Component comp)
        {
        //Si le dialogue est crée, on l'affiche en le centrant par rapport au comp
        if(dialog != null)
          {
          dialog.setLocationRelativeTo(comp);
          dialog.startProcessing();
          dialog.setVisible(true);
          }
        else {System.err.println("This dialog requires you to call initialize before !!");}
        }
    Le constructeur ne fait que créer une interface graphique dont on se moque.

    Le traitement long est lancé par la méthode startProcessing() qui est lancée lors de la méthode showDialog().

    Voici le code de la méthode startProcessing():

    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
     
    public void startProcessing()
        {
        // Activation du Thread
        task.start();
     
        // tant que le thread est en vie...
        while( task.isAlive() )
          {
          // faire un traitement...
          progressBar.setValue(progressBar.getValue() + 1);
          System.out.println("Ligne affichée par le main");
     
          try {Thread.sleep(800);}
          catch (InterruptedException ex) {}
          }
        }
    Et voici le code de la tache simulant le traitement long:

    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
    public class JAbstractTask extends Thread
      {
      public void run()
        {
        long start = System.currentTimeMillis();
     
        // boucle tant que la durée de vie du Thread est < à 1 secondes
        while( System.currentTimeMillis() < ( start + (1000 * 1)))
          {
          // traitement
          System.out.println("Ligne affichée par le thread");
          try
            {
            // pause
            Thread.sleep(500);
            }
          catch (InterruptedException ex) {}
          }
        }
      }
    Les threads fonctionnent bien en // puisque je vois les println des 2 traitements, celui de la tache simulant le long traitement et celui de la méthode startProcessing().

    Donc c'est bien le thread gérant l'affichage qui ne répond plus mais pourquoi ? et surtout comment corriger ça ?

    Merci.
    A+

  15. #15
    Membre éprouvé
    Avatar de Janitrix
    Inscrit en
    Octobre 2005
    Messages
    3 391
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 3 391
    Par défaut
    En fait, ton code est assez mal construit je trouve. Tu lances le processus long en même temps que tu affiches le WaitWindow, alors que je pense qu'il faudrait affichier le dialog, puis dans un autre thread que l'edt lancer le processus long.

    De plus, tu ne devrais pas gérer le processus long dans la classe dédié à la WaitWindow, ce sont deux choses très différentes, l'une est graphique et l'autre est une tâche potentiellement longue. Tu ne dois laisser dans la classe WaitWindow que ce qui ne concerne la gestion des graphismes. Et c'est dans la classe principale (celle qui contient ton actionPerformed en l'occurrence) que tu vas, afficher la fenêtre d'attente, puis lancer le processus.

    Il faut bien séparer le travail : les graphismes d'un côté, et les tâches de l'autre.

  16. #16
    Membre Expert
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Par défaut
    Tout ce que dit Janitrix est juste.

    J'ajoute que, si ta GUI est bloquée, c'est simplement à cause que tu fais un while / sleep dans startProcessing(). Ce startProcessing(), autant que je puisse le voir, est en plein dans le dispatching thread, donc il bloque ta GUI.

    J'ai l'impression que tu n'es pas encore à l'aise avec les threads. Ne t'inquiète pas, cela viendra. Pour progresser, une suggestion : évite toute forme de sleep. (ah ! ah ! ) Les sleep sont les caches misère de threads mal foutus. Il faut travailler à partir de synchronized, réveil sur événement, etc. C'est une gymnastique qui devient un réflexe avec un peu d'entrainement.

  17. #17
    Expert confirmé
    Avatar de le y@m's
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2005
    Messages
    2 636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Février 2005
    Messages : 2 636
    Par défaut
    As tu lu les tutoriels que je t'avais indiqué ?
    Tous ces problèmes y sont abordés et expliqués.
    Je ne répondrai à aucune question technique par MP.

    Pensez aux Tutoriels et aux FAQs avant de poster ;) (pour le java il y a aussi JavaSearch), n'oubliez pas non plus la fonction Rechercher.
    Enfin, quand une solution a été trouvée à votre problème
    pensez au tag :resolu:

    Cours Dvp : http://ydisanto.developpez.com
    Blog : http://yann-disanto.blogspot.com/
    Page perso : http://yann-disanto.fr

  18. #18
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 19
    Par défaut
    Salut à tous,

    Merci pour vos réponses.

    En fait, ton code est assez mal construit je trouve. Tu lances le processus long en même temps que tu affiches le WaitWindow, alors que je pense qu'il faudrait affichier le dialog, puis dans un autre thread que l'edt lancer le processus long.

    De plus, tu ne devrais pas gérer le processus long dans la classe dédié à la WaitWindow, ce sont deux choses très différentes, l'une est graphique et l'autre est une tâche potentiellement longue. Tu ne dois laisser dans la classe WaitWindow que ce qui ne concerne la gestion des graphismes. Et c'est dans la classe principale (celle qui contient ton actionPerformed en l'occurrence) que tu vas, afficher la fenêtre d'attente, puis lancer le processus.

    Il faut bien séparer le travail : les graphismes d'un côté, et les tâches de l'autre.
    En faite, je crois que j'ai compris pourquoi ça ne fonctionne pas, c'est juste parce que je suis un peu con ! si, si !!
    L'erreur est comme vous le dite dans la méthode startProcessing(), il suffit que cette méthode lance le thread représentant le traitement long et qu'elle rende la main comme ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    public void startProcessing()
        {
        // Activation du Thread
        task.setWaitWindow(this);
        task.start();
        }
    et surtout ne plus attendre que le thread qui simule le traitement long, sinon effectivement cela ne sert à rien de faire un thread !!

    C'est le thread (ou la tache longue) qui se charge de mettre à jour la progress bar et les messages (la WaitWindow est envoyé en paramètre avant de lancer la tache).

    Le code fonctionne et me parait propre, est-ce votre avis ?

    Merci.
    A+

    P.S.: le y@m's, oui j'ai lu les tutoriels mais je ne comprenais pas pour autant pourquoi ça ne fonctionnait pas ! Entre (), l'exemple de Romain Guy est vraiment très sympa !

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

Discussions similaires

  1. [Multi-Thread] Mettre un msg d'attente lors d'un traitement long
    Par pepito62 dans le forum Composants VCL
    Réponses: 3
    Dernier message: 29/03/2013, 21h43
  2. MVVM: Erreur thread appelant doit être en mode STA
    Par Oberown dans le forum Windows Presentation Foundation
    Réponses: 5
    Dernier message: 05/12/2010, 16h20
  3. [Thread] Travail en parallèle et attente
    Par Jabbal'H dans le forum Concurrence et multi-thread
    Réponses: 5
    Dernier message: 13/10/2008, 17h28
  4. Réponses: 1
    Dernier message: 02/06/2008, 19h35
  5. [MFC] Creation d'un thread pour un popup d'attente
    Par firejocker dans le forum MFC
    Réponses: 8
    Dernier message: 07/02/2006, 10h15

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