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

  1. #1
    Membre du Club
    Inscrit en
    février 2005
    Messages
    242
    Détails du profil
    Informations personnelles :
    Âge : 28

    Informations forums :
    Inscription : février 2005
    Messages : 242
    Points : 63
    Points
    63
    Par défaut Implémenter correctement SwingWorker pour une classe de traitement
    Bonsoir,

    J'ai créer une application qui fait du "parsing" sur des fichiers textes écrit dans un format particulier et je souhaite implémenter un SwingWorker pour pouvoir rafraîchir mon UI de façon propre et efficace.

    La méthode de parsing de ma classe lit un fichier ligne par ligne (grâce à la classe Scanner) mais ne retourne pas de résultat. Elle stocke le traitement sous forme d'une collection d'objets dans un membre de la classe, et fournit des méthodes pour générer les ListModel et TableModel qui permettront de remplir la JList et la JTable de mon interface.

    Voici les signatures des méthodes de ma classe

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    <constructeur> (String cheminFichier) {...}
    public ListModel getListeBlocs() {...}
    public TableModel getBlocModel(int index) {...}
    public boolean estChargé() {...}
    void chargerFichier() {...}
    On fournit le chemin du fichier à traiter dans le constructeur de l'objet. Ensuite on exécute la méthode chargerFichier() qui effectue le traitement à proprement dit.

    Voici un peu la struture de la méthode de traitement :

    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 chargerFichier() {
     
    	[...]
    	int nbBlocs = 0;
     
    	[...]
     
    	while (scanner.hasNextLine()) {
    		String ligne = scanner.nextLine().trim();
     
    		[...Traitement...]
     
    		nbBlocs++;					
    	}
     
    	[...]
    }
    L'idée pour moi, c'est de mettre le contenu de la variable nbBlocs dans un JLabel de mon interface (affichage du nombre de blocs traités).

    J'ai donc fait dériver ma classe de traitement de SwingWorker, déclarer la méthode doInBackground() comme ceci.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    public Object doInBackground() throws Exception {
    	this.chargerFichier();
    	return null;
    }
    et remplacer dans le traitement :

    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    setProgress(nbBlocs++);
    Le problème c'est que, dans mon interface graphique, quand je clique sur le bouton pour commencer le traitement, mon code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    btnExtraire.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent ev) {
    				if (!textChemin.getText().isEmpty()) {
    					extObj.execute();
     
    					listeBlocs.setModel(extObj.getListeBlocs());
     
    				} else {
    					JOptionPane.showMessageDialog(null, "Veuillez spécifier un fichier d'entrée.", "Avertissement", JOptionPane.WARNING_MESSAGE);
    				}
    			}
    		});
    La ligne : listeBlocs.setModel(extObj.getListeBlocs()); provoque une erreur me disant que l'argument passé à setModel() est null. J'ai l'impression que l'instruction est exécutée avant que le traitement appelé par execute() n'est terminée.

    Je fais sûrement fausse route quelque part, je ne maîtrise par trop cet aspect. Si vous avez une idée pour implémenter ça, je suis preneur.

    En espérant avoir été clair.

    Merci d'avance.

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2007
    Messages
    25 467
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2007
    Messages : 25 467
    Points : 48 756
    Points
    48 756
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    listeBlocs.setModel(extObj.getListeBlocs());
    doit se trouver dans la méthode done de ton SwingWorker, puisque ça se fait après la fin du traitement. La méthode execute() retourne immédiatement, le but du swingworker est de ne pas bloquer l'EDT dont forcément, elle ne va pas attendre que le doInbackground soit terminé.

  3. #3
    Membre du Club
    Inscrit en
    février 2005
    Messages
    242
    Détails du profil
    Informations personnelles :
    Âge : 28

    Informations forums :
    Inscription : février 2005
    Messages : 242
    Points : 63
    Points
    63
    Par défaut
    Bonjour

    Tout d'abord, merci pour la réponse.

    Le truc c'est que je ne veux pas mélanger trop la classe Métier avec la classe de mon interface graphique. Puis-je notifier la fin de l'opération à mon interface graphique avec une méthode ?

    Merci

  4. #4
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2007
    Messages
    25 467
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2007
    Messages : 25 467
    Points : 48 756
    Points
    48 756
    Par défaut
    ben en fait, c'est ton métier qui n'a rien à faire dans le swingworker Le swingworker est une classe destinée à l'ui.

    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
    btnExtraire.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent ev) {
    				if (!textChemin.getText().isEmpty()) {
                                            new SwingWorker<Void,Void>(){
                                                   @Override
                                                   public Void doInBackground() {
                                                       extObj.chargerFichier();
                                                       return null;
                                                   }
                                                   @Override
                                                   protected void done() {
                                                       listeBlocs.setModel(extObj.getListeBlocs());
                                                   }
                                            }.execute();
    				} else {
    					JOptionPane.showMessageDialog(null, "Veuillez spécifier un fichier d'entrée.", "Avertissement", JOptionPane.WARNING_MESSAGE);
    				}
    			}
    		});

  5. #5
    Membre du Club
    Inscrit en
    février 2005
    Messages
    242
    Détails du profil
    Informations personnelles :
    Âge : 28

    Informations forums :
    Inscription : février 2005
    Messages : 242
    Points : 63
    Points
    63
    Par défaut
    Bonjour,

    Haha c'est marrant, j'ai fait parfaitement l'inverse. D'ailleurs j'ai réussi à bricoler un truc qui, après lecture de ton message et réflexion, doit finalement s'avérer très moche. xD Mais qui marche ! =) (à peu près)

    C'est pourtant pas faute d'avoir lu les tutos.

    Je vais essayer de voir pour réécrire ça. Je vous tiens au courant.

    Merci en tout cas ^^'

  6. #6
    Membre du Club
    Inscrit en
    février 2005
    Messages
    242
    Détails du profil
    Informations personnelles :
    Âge : 28

    Informations forums :
    Inscription : février 2005
    Messages : 242
    Points : 63
    Points
    63
    Par défaut
    Re,

    Bon, j'ai nettoyé toute ma m.... , j'ai mis ton code à la place. Ça marche bien, mais comment je fais pour mettre à jour la barre de progression suivant l'avancée du traitement ? Je ne peux pas utiliser setProgress() et publish() dans la méthode de traitement puisque ma classe métier n'hérite plus de SwingWorker.

    Il faut bien que ma classe métier remonte les infos sur l'avancée du traitement (pourcentage, nombres de lignes traitées, nombre de blocs, ...) à mon UI.

    Une idée ?

    Merci.

  7. #7
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2007
    Messages
    25 467
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2007
    Messages : 25 467
    Points : 48 756
    Points
    48 756
    Par défaut
    Soit tu découpe ton processus en étape et tu appelle les étapes une à une depuis ton UI: c'est vite moche et ça amène de la logique buisness dans l'ui, soit tu modifie ta méthode buisness pour qu'elle prenne en paramètre un listener, qui recevra les rapport d'avancement. Tu pourrais par exemple en java8 utiliser un Consumer


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void chargerFichier(Consumer<Integer> listener){
    ....
    	while (scanner.hasNextLine()) {
    		String ligne = scanner.nextLine().trim();
     
    		[...Traitement...]
     
    		nbBlocs++;	
                    listener.accept(nbBlocs);
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
                                                   public Void doInBackground() {
                                                       extObj.chargerFichier((status)->publish(status));
                                                       return null;
                                                   }

  8. #8
    Membre du Club
    Inscrit en
    février 2005
    Messages
    242
    Détails du profil
    Informations personnelles :
    Âge : 28

    Informations forums :
    Inscription : février 2005
    Messages : 242
    Points : 63
    Points
    63
    Par défaut
    Bonjour,

    Merci pour la solution, ça marche nickel. J'ai dû me renseigner un peu sur le Consumer<> qui à priori sert à passer, entre autres, un lambda à une méthode.
    Ça fait très propre et c'est assez pratique.

    Mais si je suis curieux jusqu'au bout, comment aurais-je fait sans Java 8?

    Merci

  9. #9
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2007
    Messages
    25 467
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2007
    Messages : 25 467
    Points : 48 756
    Points
    48 756
    Par défaut
    l'interface Customer n'existant pas tu aurais du faire une interface similaire. Ce n'est jamais qu'une interface avec une méthode.

    Ensuite comme y a pas de lambda, tu aurais du créer une classe anonyme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
                                                   public Void doInBackground() {
                                                       extObj.chargerFichier(new MyCustomer<Integer>(){
                                                          public void consume(Integer value){
                                                              publish(value);
                                                          }
                                                       });
                                                       return null;
                                                   }
    C'est juste plus verbeux, le principe restant le même.

  10. #10
    Membre du Club
    Inscrit en
    février 2005
    Messages
    242
    Détails du profil
    Informations personnelles :
    Âge : 28

    Informations forums :
    Inscription : février 2005
    Messages : 242
    Points : 63
    Points
    63
    Par défaut
    Bonjour tchize,

    oui c'est un peu ce que j'avais fait à la base mais c'est la classe de traitement qui héritait de SwingWorker et la JFrame qui redéfinissait les méthodes abstraites de mon interface. Mais c'est beaucoup mieux comme ça. Le SwingWorker ne se retrouve pas dans la classe métier et c'est beaucoup plus propre.

    Merci beaucoup pour la solution et les explications surtout.

    A bientôt.

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

Discussions similaires

  1. Définition de méthodes pour une classe template
    Par Pragmateek dans le forum Langage
    Réponses: 13
    Dernier message: 20/12/2008, 01h46
  2. Affichage bizarre pour une classe lettre
    Par Aline2611 dans le forum Mise en forme
    Réponses: 2
    Dernier message: 07/08/2006, 10h27
  3. Héritage d'un événement pour une classe fille
    Par korntex5 dans le forum Langage
    Réponses: 4
    Dernier message: 11/01/2006, 17h48
  4. Réponses: 8
    Dernier message: 02/11/2005, 21h21
  5. ecrire un iterateur pour une classe
    Par Blowih dans le forum C++
    Réponses: 2
    Dernier message: 15/11/2004, 20h19

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