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 :

Bonne méthode pour Swing


Sujet :

EDT/SwingWorker Java

  1. #1
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 466
    Points : 333
    Points
    333
    Par défaut Bonne méthode pour Swing
    Bonjour,

    Soit un programme qui utilise une fenêtre crée via une 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
     
    public class Main
    {
    	/**
             * @param args
             */
    	public static void main(String[] args)
    	{
    		javax.swing.SwingUtilities.invokeLater(new Runnable() 
            {
    			public void run() 
                {
                	new Fenetre();
                }
            });
     
    	}
    }
    la création de la fenêtre commence par une initialisation des données du programme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    .../...
    	/**
             * le constructeur
             */
    	public Fenetre() 
    	{
            //initialisation des paramètres
    		this.gd = new GestionDonnees();
     
    .../...
    ma question est la suivante
    vaut-il mieux sortir new GestionDonnees() de la création de la fenêtre ou est-il de bonne programmation de laisser ainsi ?
    Voici l'alternative à laquelle je pense

    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
     
    public class Main
    {
    	/**
             * @param args
             */
    	public static void main(String[] args)
    	{
    		final GestionDonnees gd = new GestionDonnees();
    	    javax.swing.SwingUtilities.invokeLater(new Runnable() 
            {
    			public void run() 
                {
                	new Fenetre(gd); 
                }
            });
     
    	}
    }
    Les deux marchent bien sûr mais quelle est la méthode la plus appropriée ?

  2. #2
    Membre averti
    Homme Profil pro
    Inscrit en
    Avril 2011
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Avril 2011
    Messages : 214
    Points : 338
    Points
    338
    Par défaut
    Bonjour,

    Ça n'a une importance que si vous voulez exécuter le code Swing et le code du modèle (des données) dans des threads séparés.

    Si tout se passe dans le thread EDT (plus simple à mettre en œuvre mais bloque l'UI pendant le traitement des données ce qui n'est pas forcément agréable pour l'utilisateur si les traitements sont longs) autant prendre la méthode la plus simple (la 1ère).

    Ceci-dit, dans n'importe quel cas si l'initialisation de la vue ET du modèle sont longues, vous pouvez gagner du temps au démarrage en les parallélisant, par exemple :
    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
     
    public static void main(String[] args) {
      final Fenetre[] fenetre = new Fenetre[1];
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          fenetre[0] = new Fenetre(); // longue opération
        }
      });
     
      final GestionDonnees gd = new GestionDonnees(); // longue opération
     
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          fenetre[0].setGestionDonnees(gd);
          fenetre[0].setVisible(true);
        }
      });
     
    }
     
    // dans la classe "Fenetre"
    public void setGestionDonnees(GestionDonnees gd) {
      this.gd = gd;
      // ...
    }

  3. #3
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 466
    Points : 333
    Points
    333
    Par défaut
    Citation Envoyé par -gma- Voir le message
    Bonjour,

    Ça n'a une importance que si vous voulez exécuter le code Swing et le code du modèle (des données) dans des threads séparés.

    Si tout se passe dans le thread EDT (plus simple à mettre en œuvre mais bloque l'UI pendant le traitement des données ce qui n'est pas forcément agréable pour l'utilisateur si les traitements sont longs) autant prendre la méthode la plus simple (la 1ère).

    Ceci-dit, dans n'importe quel cas si l'initialisation de la vue ET du modèle sont longues, vous pouvez gagner du temps au démarrage en les parallélisant, par exemple :
    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
     
    public static void main(String[] args) {
      final Fenetre[] fenetre = new Fenetre[1];
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          fenetre[0] = new Fenetre(); // longue opération
        }
      });
     
      final GestionDonnees gd = new GestionDonnees(); // longue opération
     
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          fenetre[0].setGestionDonnees(gd);
          fenetre[0].setVisible(true);
        }
      });
     
    }
     
    // dans la classe "Fenetre"
    public void setGestionDonnees(GestionDonnees gd) {
      this.gd = gd;
      // ...
    }
    Bonjour,
    merci d'avoir répondu.
    Je n'avais pas pensé effectivement à paralléliser la création de la fenêtre et la création de la gestion de données.
    Par contre dans la réponse je ne comprends pas pourquoi on passe par un tableau de 1 Fenetre.

  4. #4
    Membre averti
    Homme Profil pro
    Inscrit en
    Avril 2011
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Avril 2011
    Messages : 214
    Points : 338
    Points
    338
    Par défaut
    Citation Envoyé par Patrice Henrio Voir le message
    Bonjour,
    Par contre dans la réponse je ne comprends pas pourquoi on passe par un tableau de 1 Fenetre.
    Pour que la "fenetre" soit accessible dans les classes anonymes (Runnable) il faut que la variable soit déclarée comme finale.
    Mais si la variable était marquée "final" on ne pourrait pas l'assigner dans le premier Runnable.

    Passer par un tableau permet de contourner ce problème même si c'est moyennement propre.

  5. #5
    Modérateur
    Avatar de wax78
    Homme Profil pro
    Chef programmeur
    Inscrit en
    Août 2006
    Messages
    4 085
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chef programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 4 085
    Points : 8 004
    Points
    8 004
    Par défaut
    Dans ce cas, moi je prendrai :

    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
     
     
    public class Fenetre extends JFrame {
     
    	GestionDonnees gd = null;
    	JLabel label;
     
    	public Fenetre()
    	{
    		// Crée une fenêtre avec un truc d'attente.
    		label = new JLabel("J'ai pas encore fini l'init du modèle monsieur");
    		getContentPane().add(label);
    		setSize(640,480);
    		setVisible(true);
     
    	}
     
    	public void setGestionDonnees(GestionDonnees gd) {
    		this.gd=gd;
    		inittout();
    	}
     
    	private void inittout() {
    		// Crée reelement l'interface parce que le modèle a fini son "calcul".
    		String str = this.gd.getToto(); // Bah oui je veux Toto pour l'afficher dans un label :)
    		label.setText(str);
    	}
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class GestionDonnees {
     
    	String toto;
     
    	public GestionDonnees() throws InterruptedException {
    		Thread.sleep(5000); // Une longue init;
    		toto = "ÏchBinToto"; // Resultat de la longue init
    	}
     
    	public synchronized String getToto() {
    		return toto;
    	}
     
    }
    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
    import javax.swing.SwingUtilities;
     
    public class main_A {
     
    	public static void main(String args[]) {
    		final Fenetre[] fenetre = new Fenetre[1];
    		SwingUtilities.invokeLater(new Runnable() {
    			public void run() {
    				fenetre[0] = new Fenetre(); // longue opération
    			}
    		});
     
    		Thread thr = new Thread() {
    			public void run() {
     
    				try {
    					final GestionDonnees gd;
    					gd = new GestionDonnees();
     
    					SwingUtilities.invokeLater(new Runnable() {
    						public void run() {
    							System.err.println("==" + fenetre[0]);
    							fenetre[0].setGestionDonnees(gd);
    						}
    					});
     
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
     
    			}
    		};
    		thr.start();
    	}
     
    }
    Comme ca, le gestionnaire de donnée calcul tranquillement.
    On le passe une fois qu'il est "construit" à l'interface, qui affichait un message d'attente entretemps pour ensuite affiche "c'est fini".

    Pas de blocage de l'interface, le modele se construit en arrière plan. Et surtout, pas de nullpointer ca avec ton modèle originale gma cela pouvait se produire dans certains cas (dont celui que j'expose).

    Après ca reste mon avis, jeté a l'arrache après journée. Il y'a surement mieux mais je viens de l'imaginer. Évidemment il ne faut pas que le constructeur de la frame bloque sinon ca ne sert a rien

  6. #6
    Membre averti
    Homme Profil pro
    Inscrit en
    Avril 2011
    Messages
    214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Avril 2011
    Messages : 214
    Points : 338
    Points
    338
    Par défaut
    Citation Envoyé par wax78 Voir le message
    Il est possible d'avoir besoin de données du modèle lors de la construction de l'interface alors que le modèle ne serait même pas encore passé a l'interface si tu vois ce que je veux dire. Avec contournement il y'a moyen d'utiliser ce modèles cependant.
    En effet, dans ce cas là il faut mettre cette partie du code dans le setter :

    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 Fenetre extends JFrame {
     
    	private GestionDonnees gd;
     	private final JLabel label;
    	public Fenetre()
    	{
    		label = new JLabel(str);
    		getContentPane().add(label);
    	}
     
    	public synchronized void setGestionDonnees(GestionDonnees gd) {
    		this.gd=gd;
    		init();
    	}
     
    	private init() {
    		label.setText(this.gd.getToto());
    	}
    }
    Le mieux étant d'ajouter des "'listeners" pour réduire le couplage mais on s'éloigne du sujet

  7. #7
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 466
    Points : 333
    Points
    333
    Par défaut
    Citation Envoyé par -gma- Voir le message
    Pour que la "fenetre" soit accessible dans les classes anonymes (Runnable) il faut que la variable soit déclarée comme finale.
    Mais si la variable était marquée "final" on ne pourrait pas l'assigner dans le premier Runnable.

    Passer par un tableau permet de contourner ce problème même si c'est moyennement propre.
    De mémoire j'avais vu ce genre de choses dans une vieille discussion mais pour des variables de type de base (int, double ...). Comme il fallait dans certains cas une référence à la variable on passait par un tableau.
    au lieu de
    final int i

    on avait

    final int[1] i

    et on utilisait i[0] au lieu de i

    Depuis je crois que pour le même problème on peut utiliser Integer, Double ... etc. D'ailleurs à l'époque j'avais compris la raison du tableau mais pas pourquoi on n'utilisait pas les classes correspondantes.

    Je ne connaissais pas ce problème pour l'assignation d'une variable dans une classe anonyme.
    C'est en effet moyennement propre.

  8. #8
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 466
    Points : 333
    Points
    333
    Par défaut
    Pour la méthode de wax78.
    Pourquoi créer un thread pour l'initialisation des données alors que sans cela ce sera fait dans le Thread main ? Plus simplement, je verrai cela (j'ai mis en commentaire ce qui me paraissait superflu)

    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
     
    import javax.swing.SwingUtilities;
     
    public class main_A {
     
    	public static void main(String args[]) {
    		final Fenetre[] fenetre = new Fenetre[1];
    		SwingUtilities.invokeLater(new Runnable() {
    			public void run() {
    				fenetre[0] = new Fenetre(); // longue opération
    			}
    		});
     
    //		Thread thr = new Thread() {
    //			public void run() {
     
    				try {
    					final GestionDonnees gd;
    					gd = new GestionDonnees();
     
    					SwingUtilities.invokeLater(new Runnable() {
    						public void run() {
    							System.err.println("==" + fenetre[0]);
    							fenetre[0].setGestionDonnees(gd);
    						}
    					});
     
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
     
    //			}
    //		};
    //		thr.start();
    	}
     
    }

  9. #9
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 466
    Points : 333
    Points
    333
    Par défaut
    Il y a des choses que je n'ai pas encore compris mais ça avance. En plus le développement de la discussion rentre en plein sur un problème que j'ai dans un autre projet. Pour schématiser je crée une fenêtre Fenetre avec des composants (dont un bouton par exemple), en parallèle je créer une classe (ce pourrait être GestionDonnees) qui a besoin du bouton. Il y donc un besoin de Synchronized là dedans. Mais où dois-je le placer ?

  10. #10
    Modérateur
    Avatar de wax78
    Homme Profil pro
    Chef programmeur
    Inscrit en
    Août 2006
    Messages
    4 085
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chef programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 4 085
    Points : 8 004
    Points
    8 004
    Par défaut
    "C'est pour voir si tu suivait".

    En effet,le thread n'a aucune utilité dans ce cas, je me suis planté en chemin, autant pour moi.

  11. #11
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 466
    Points : 333
    Points
    333
    Par défaut
    Citation Envoyé par wax78 Voir le message
    "C'est pour voir si tu suivait".

    En effet,le thread n'a aucune utilité dans ce cas, je me suis planté en chemin, autant pour moi.
    Bon j'ai progressé sur les Thread alors.

    merci d'avoir testé ma capacité à les comprendre. C'est un sujet que j'ai eu du mal à bien assimiler.

    (ma question sur le synchronized montre que c'est pas encore acquis)

  12. #12
    Modérateur
    Avatar de wax78
    Homme Profil pro
    Chef programmeur
    Inscrit en
    Août 2006
    Messages
    4 085
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chef programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 4 085
    Points : 8 004
    Points
    8 004
    Par défaut
    Non je blaguais, hein, je me suis planté

    Sinon pour le synchronized, voici quelques infos qui te permettront peut-être de voir :

    http://docs.oracle.com/javase/tutori.../syncmeth.html
    http://rom.developpez.com/java-synchronisation/

  13. #13
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 466
    Points : 333
    Points
    333
    Par défaut
    Citation Envoyé par wax78 Voir le message
    Non je blaguais, hein, je me suis planté

    Sinon pour le synchronized, voici quelques infos qui te permettront peut-être de voir :

    http://docs.oracle.com/javase/tutori.../syncmeth.html
    http://rom.developpez.com/java-synchronisation/
    J'avais déjà lu plusieurs fois la deuxième référence. Pour la première je m'y mets. en attendant j'ai fait comme cela.

    Dans la classe fenetre, j'ai créé un booléen fini initialisé à false et qui passe à true lors de la dernière instruction du constructeur (une sorte de sémaphore, j'ai pas compris pourquoi les sémaphores de java étaient des entiers comme indiqué dans Java et la synchronisation <citation>Un sémaphore encapsule un entier, avec une contrainte de positivité, et deux opérations atomiques d'incrémentation et de décrémentation.</citation>
    J'ai créé bien entendu un accesseur isFini.
    La classe principale utilise SwingUtilities pour créer la fenêtre, puis continue tranquillement son petit bonhomme de chemin. Lorsque on a besoin du bouton
    j'utilise
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    while(! fenIsReady())
    {
    	try
    	{
    	        Thread.currentThread().sleep(500);
    	}
    	catch (InterruptedException e)
    	{
    		e.printStackTrace();
    	}
    }
    System.out.println("le bouton : " + this.fenetrePrincipale.btnTest.getText() + "est prêt");
    avec la méthode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    private boolean fenetreIsReady()
    {
            if (this.fenetrePrincipale == null) return false;
            return this.fenetrePrincipale.isFini;
    }

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 23/04/2008, 16h41
  2. Réponses: 2
    Dernier message: 14/03/2008, 10h57
  3. Réponses: 7
    Dernier message: 06/08/2007, 20h28
  4. Bonne méthode pour parser mon xml
    Par scaleo dans le forum Windows Forms
    Réponses: 2
    Dernier message: 09/07/2007, 10h04
  5. Réponses: 2
    Dernier message: 22/08/2006, 09h28

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