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 :

JFrame vide EDT et Thread


Sujet :

EDT/SwingWorker Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2008
    Messages
    254
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Janvier 2008
    Messages : 254
    Par défaut JFrame vide EDT et Thread
    Bonjour,

    J'ai une application dans une JFrame et je mets l'affichage d'une JFrame (contenant une image, une JProgressBar et du texte d'information) dans un Thread séparé...

    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
     
    final FrameSplash test = new FrameSplash ();
     
    new Thread(new Runnable() {
        @Override
        public void run() {
            test.setVisible(true);
        }
    }).start();
     
    setUpOnglet1();
    test.setProgress(10);
    setUpOnglet2();
    test.setProgress(20);
    .
    .
    .
    setUpOnglet9();
    test.setProgress(100);
     
    test.setVisible(false);
    Mais le problème est que "test" est vide jusqu'à ce que tous les onglets soient construit.

    Je n'arrive pas à comprendre pourquoi...

    Merci d'avance.

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Bonjour,

    Il sera difficile de t'aider si tu on ne sait pas ce qui est fait dans les différentes méthodes que tu appelles.

    De plus, faire un setVisible() hors EDT est très dangereux et sujet à de potentiels problèmes.

    Cdlt

  3. #3
    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
    Attention que ce n'est pas dans n'importe quel thread séparé qu'il faut intervenir sur swing, mais dans le thread dit EDT. Voir Qu'est ce que l'Event Dispatch Thread (EDT) ?

  4. #4
    Membre éclairé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2008
    Messages
    254
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Janvier 2008
    Messages : 254
    Par défaut
    C'est pas évident de mettre tout ce que font les méthodes setUpOnglet. Qlq explications s'imposent donc.

    Je travaille avec NetBeans qui me génère un "initComponents". Après son appel j'appel une méthode monInitComponents et dans celle ci les méthodes setUpOnglet.

    Dans ma FrameView générée par NetBeans j'ai un jTabbedPane qui contient des JPanel qui eux-mêmes contiennent des JTable avec des données venant de SELECT SQL avec une connection JDBC pour chaque table SQL correspondante.

    Donc dans monInitComponents les setUpOnglet me permettent de construire tout cela au démarrage de l'application et cela prends un certain temps.

    Je voudrais donc avec une JFrame faire un splashScreen avec une JProgressBar que je mettrai à jour après la construction de chaque onglet de mon jTabbedPane.

    Je m'y prends apprement très mal...

    Peut-être une autre façon de faire dans monInitComponents serait :

    -affichage de JFrame avec JProgressBar
    -création d'un Thread dans lequel j'appele les setUpOnglet et les mise à jour de la JProgressBar
    -fermeture de JFrame avec JProgressBar

    Merci de m'aider.

  5. #5
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,


    Ton problème vient du fait que tu ne respectes pas les règles de l'EDT :
    • Tout traitement modifiant l'interface graphique (changement d'état, ajout/suppression de composant, etc.) DOIT OBLIGATOIREMENT se faire dans l'EDT, afin de respecter la règle du mono-thread.

      C'est à dire que les composants ne sont géré que par un seul thread (l'EDT).
      Pour cela tu peux utiliser SwingUtilities.isEventDispatchThread() (pour savoir si tu es dans l'EDT) et SwingUtilities.invokeLater() (pour exécuter du code dans l'EDT.
      En règle général toutes les méthodes évènementielle (actionPerformed(), mouseClicked(), etc.) sont exécuté dans l'EDT (c'est lui qui gère les évènements).
    • Tout traitement "lourd" ou potentiellement bloquant DOIT être exécuté dans un thread distinct de l'EDT.
      L'EDT traite toutes ses actions en boucles, et si tu y fait des traitements lourds, cela retardera tout autant ses opérations. Or l'EDT se charge de mettre à jour l'affichage et de la gestion des évènements. Si tu y effectues un traitement long tout ceci sera retardé...
      Pour éviter cela tu dois bien sûr utiliser des threads...



    En clair :
    • Tu dois effectuer ton traitements dans un thread.
    • Mais mettre à jour ton interface (état du progressbar, mise à jour des données, etc.) dans l'EDT.




    Pour simplifier tout ceci il faut se tourner vers la classe SwingWorker.

    Petit exemple commenté :
    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
    class TestFrame extends JFrame {
     
    	private final DefaultListModel model = new DefaultListModel();
    	private final JList list = new JList(model);
    	private final JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    	private final JButton button = new JButton("Go");
     
    	public TestFrame() {
    		super("Test");
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setSize(400, 400);
    		setLocationRelativeTo(null);
     
     
    		button.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				buttonPerformed();
    			}
    		});
    		panel.add(button);
     
    		ImageIcon icon = null;
    		try {
    			icon = new ImageIcon(new URL("http://adiguba.developpez.com/blog/duke.running.gif"));
    		} catch (MalformedURLException e1) {
    			e1.printStackTrace();
    		}
     
    		getContentPane().add(new JScrollPane(list));
    		getContentPane().add(panel, BorderLayout.SOUTH);
    		getContentPane().add(new JLabel(icon), BorderLayout.EAST);
    	}
     
     
    	private void buttonPerformed() {
     
    		// Cette méthode est appelée par actionPerformed()
    		// On est donc dans l'EDT on peut mettre à jour notre interface :
    		final JProgressBar progress = new JProgressBar(0, 100);
    		progress.setStringPainted(true);
     
     
    		// On grise le bouton :
    		button.setEnabled(false);
    		// Et on ajoute une JProgressBar :
    		panel.add(progress);
    		panel.revalidate();
     
     
    		// On crée notre SwingWorker qui va effectuer le traitement :
    		SwingWorker<Long, Double> worker = new SwingWorker<Long, Double>() {
     
    			/**
                             * La méthode doInBackground() permet d'effectuer
                             * les traitements lourds.
                             * Cette méthode est toujours exécutée dans un thread séparé.
                             * On ne doit pas y modifier l'interface graphique
                             */
    			@Override
    			protected Long doInBackground() throws Exception {
    				long time = System.currentTimeMillis();
     
    				for (int i=0; i<10; i++) {
    					// setProgress() permet d'indiquer l'état du SwingWorker,
    					// et de le coupler avec une JProgressBar (voir plus loin)
    					setProgress(i*10);
     
    					// On simule un traitement 'lent' en endormant le thread :
    					Thread.sleep((int)(Math.random()*1000));
     
    					// On génère une valeur aléatoire :
    					Double value = Math.random();
     
    					// On utilise publish() pour publier la valeur
    					// (voir la méthode process() ci-dessous)
    					publish(value);
    				}
    				setProgress(100);
     
    				time = System.currentTimeMillis() - time;
     
    				// On peut eventuellement retourné une valeur
    				// (ici la durée du traitement)
    				return time;
    			}
     
     
    			/**
                             * Cette méthode sera toujours exécuté dans l'EDT.
                             * Elle recevra en paramètre les valeurs passées à publish().
                             * Cela peut permettre de mettre à jour l'interface pendant le traitement.
                             */
    			@Override
    			protected void process(List<Double> chunks) {
    				// Attention : plusieurs appels à publish() peuvent être regroupés.
    				// Il faut donc traiter toutes les valeurs de la liste :
    				for (Double d : chunks) {
    					model.addElement(d);
    				}
    			}
     
    			/**
                             * Cette méthode sera toujours exécuté dans l'EDT.
                             * Elle est appelée dès que la méthode doInBackground() se termine
                             * (normalement ou pas), et permet donc d'effectuer les mises à jours
                             * finale sur l'interface graphique...
                             */
    			@Override
    			protected void done() {
     
    				// On récupère la valeur de retour de doInBackground() :
    				try {
    					Long time = get();
     
    					// Tout s'est déroulé correctement !
    					JOptionPane.showMessageDialog(TestFrame.this,
    							"Durée du traitement : " + time + " ms");
     
    				} catch (ExecutionException e) {
    					// Une erreur est survenue :
    					JOptionPane.showMessageDialog(TestFrame.this, 
    							"Erreur lors du traitement : " + e.getCause().getMessage(),
    							"Erreur",
    							JOptionPane.ERROR_MESSAGE);
    				} catch (InterruptedException e) {
    					// interruption reçu pendant le get()
    					e.printStackTrace();
    				}
     
    				// On restaure le bouton :
    				button.setEnabled(true);
    				// Et on supprime la JProgressBar
    				//progress.setVisible(false);
    				panel.remove(progress);
    				panel.revalidate();
    				panel.repaint();
    			}
     
    		};
     
    		// On ajoute une listener pour suivre l'état d'avancement du Worker
    		// => cela permet de mettre à jour la barre de progression.
    		worker.addPropertyChangeListener(new ProgressPropertyChangeListener(progress.getModel()));
     
    		// Enfin on démarre réellement le SwingWorker
    		worker.execute();
     
    		// Notre actionPerformed() est terminé, et l'EDT peut reprendre son traitement normal
    		// Toutefois notre traitement ne fait que commencer dans un thread séparé ;)
    	}
     
    	public static void main(String[] args) {
    		SwingUtilities.invokeLater(new Runnable() {
    			@Override
    			public void run() {
    				new TestFrame().setVisible(true);
    			}
    		});
    	}
     
    }
    Avec la classe ProgressPropertyChangeListener qui ressemble à ceci :
    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
    class ProgressPropertyChangeListener implements PropertyChangeListener {
     
    	private final BoundedRangeModel model;
     
    	public ProgressPropertyChangeListener(BoundedRangeModel model) {
    		this.model = model;
    	}
     
    	@Override
    	public void propertyChange(PropertyChangeEvent evt) {
    		if ("progress".equals(evt.getPropertyName())) {
    			Integer progress = (Integer) evt.getNewValue();
    			this.model.setValue(progress);
    		} else if ("state".equals(evt.getPropertyName())) {
    			SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
    			switch(state) {
    			case PENDING:
    			case STARTED:
    	        	this.model.setValue(this.model.getMinimum());
    	        	break;
    			case DONE:
    	        	this.model.setValue(this.model.getMaximum());
    	        	break;
    			}
    		}
    	}
    }

    a++

  6. #6
    Membre éclairé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2008
    Messages
    254
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Janvier 2008
    Messages : 254
    Par défaut
    Problème résolu.

    J'ai utilisé un SwingWorker et dans le doInBackground je mets la construction de mes onglets.

    Merci adiGuba, c'est exactement ce que je voulais.

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

Discussions similaires

  1. Impression : EDT ou thread séparé ?
    Par kwxyz dans le forum EDT/SwingWorker
    Réponses: 2
    Dernier message: 05/07/2010, 15h25
  2. JFrame vide jusqu'à obtention d'une réponse d'un WebService ?
    Par genamiga dans le forum EDT/SwingWorker
    Réponses: 6
    Dernier message: 19/05/2009, 01h02
  3. Problème de JFrame vide
    Par elfiestador dans le forum Agents de placement/Fenêtres
    Réponses: 9
    Dernier message: 02/08/2007, 17h08
  4. JFrame vide lorsqu'elle est appelée
    Par superpigeon dans le forum Agents de placement/Fenêtres
    Réponses: 8
    Dernier message: 27/03/2007, 18h23
  5. [Swing] JFrame vide !
    Par theniaky dans le forum Agents de placement/Fenêtres
    Réponses: 5
    Dernier message: 17/05/2006, 18h08

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