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

AWT/Swing Java Discussion :

Thread + crash sur rafraîchissement table : model pas encore mis à jour?


Sujet :

AWT/Swing Java

  1. #1
    Membre éclairé
    Avatar de seiryujay
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    950
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 950
    Points : 722
    Points
    722
    Par défaut [Maj]Thread+crash table:model pas mis à jour? => ListSelectionListener et JTreeTable
    Salut!

    J'ai un petit souci. En fait, j'utilise le composant JTreeTable disponible ici. Jusqu'à présent, je n'avais aucun problème, mais depuis que j'ai ajouté des threads pour exécuter les gros traitements, j'ai des crashes (pas critiques, mais embêtants) survenant sur la supression de noeuds. Il s'agit de crashes sur le repaint(), avec notamment des ArrayOutOfBoundException dû au fait que mon modèle ne doit pas être totalement à jour.

    J'aimerai savoir comment me débrouiller pour être sûr que mon modèle est toujours à jour, et que ma JTreeTable a été prévenue des changements intervenus dans mon modèle.

    Merci d'avance.

  2. #2
    Membre chevronné
    Avatar de afrikha
    Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    1 600
    Détails du profil
    Informations personnelles :
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2005
    Messages : 1 600
    Points : 2 208
    Points
    2 208
    Par défaut
    Salut,
    je ne suis pas sùr d'avoir bien compris ton problème...

    Citation Envoyé par seiryujay
    mais depuis que j'ai ajouté des threads pour exécuter les gros traitements, j'ai des crashes (pas critiques, mais embêtants) survenant sur la supression de noeuds.
    La mise à jour de l'interface graphique doit se faire depuis l'EDT. Tu peux utiliser SwingUtilities.invokeLater ou bien SwingUtilities.invokeAndWait .
    J'aimerai savoir comment me débrouiller pour être sûr que mon modèle est toujours à jour,
    Là je ne comprends pas trop...
    et que ma JTreeTable a été prévenue des changements intervenus dans mon modèle.
    Tu dois avoir dans ton modèle une méthode du style fireContentChanged , non ?

  3. #3
    Expert éminent sénior
    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
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Il faut que toutes les modifications sur ton modèle soit effectué dans l'EDT.
    Si tu modifies le modèle depuis un autre thread, tu peux te retrouver dans un cas du style :
    • L'EDT est en train de redessiner la table (qui comporte par exemple 20 éléments).
    • Ton autre thread reprend la main et supprime un élément.
    • L'EDT tente d'accéder au 20ième élément qui n'est plus présent --> ArrayOutOfBoundException

    La classe SwingWorker pourrait t'être utile

    a++

  4. #4
    Membre éclairé
    Avatar de seiryujay
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    950
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 950
    Points : 722
    Points
    722
    Par défaut
    Citation Envoyé par adiGuba
    Salut,


    Il faut que toutes les modifications sur ton modèle soit effectué dans l'EDT.
    Si tu modifies le modèle depuis un autre thread, tu peux te retrouver dans un cas du style :
    • L'EDT est en train de redessiner la table (qui comporte par exemple 20 éléments).
    • Ton autre thread reprend la main et supprime un élément.
    • L'EDT tente d'accéder au 20ième élément qui n'est plus présent --> ArrayOutOfBoundException

    La classe SwingWorker pourrait t'être utile

    a++
    C'est exactement ce qui m'arrive je pense.
    Je vais jeter un oeil à cette classe...
    Par contre, je travaille sous JRE 1.4.2, donc cette classe ne faisait pas partie de l'API et si une meilleure gestion de mon code peut me permettre de contourner le problème, je préfère autant faire comme ça.

    Donc petite question :
    Ma JTreeTable sert à lister et à donner des informations sur des formes géométriques.
    Les feuilles correspondent à des formes simples, et les noeuds correspondent à des groupes de formes. En fait, je regroupe certaines formes suivant des critères liés à la surface, la longueur, etc des formes simples.
    Du coup, si je supprime une forme simple, il se peut qu'un groupe soit complétement détruit, et divisé en plusieurs nouveaux groupes ou formes simples, si les critères de regroupement ne sont pas respectés.
    Ca c'est le principe.

    Donc sur la suppression d'une feuille, je dois réappliquer le calcul de regroupement sur les formes contenues dans le groupe modifié. Dans ce calcul, je recrée les nouveaux groupes qui seront ensuite affichés dans ma JTreeTable => modification du model.
    Et comme ça prend du temps, j'ai fait ça dans un thread.

    Il suffit juste que je remplace ce thread par un invokeLater() pour que je n'ai plus ces crashes?
    Je vais tester, mais si vous pouvez me le confirmer...

    Merci d'avance!

  5. #5
    Expert éminent sénior
    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
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par seiryujay
    Il suffit juste que je remplace ce thread par un invokeLater() pour que je n'ai plus ces crashes?
    Je vais tester, mais si vous pouvez me le confirmer...
    Non si tu remplaces tout le thread par un invokeLater() cela n'a plus aucun intérêt car tout ton traitement sera exécuté dans l'EDT et donc cela bloquera l'affichage.

    Tu dois juste utiliser invokeLater() pour modifier le modèle.
    Ainsi ton thread se charge de tous les calculs, et lorsque il a besoin de modifier le modèle il utilisera invokeLater() pour le faire (ou invokeAndWait() s'il a besoin d'attendre la fin du traitement).

    Le plus simple serait de te faire une série de méthode qui effecuerons la tâche dans l'EDT, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        public void removeFromModel(final int index) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    monModel.remove(index);
                }
            });
        }
    a++

  6. #6
    Membre éclairé
    Avatar de seiryujay
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    950
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 950
    Points : 722
    Points
    722
    Par défaut
    Citation Envoyé par adiGuba
    Non si tu remplaces tout le thread par un invokeLater() cela n'a plus aucun intérêt car tout ton traitement sera exécuté dans l'EDT et donc cela bloquera l'affichage.

    Tu dois juste utiliser invokeLater() pour modifier le modèle.
    Ainsi ton thread se charge de tous les calculs, et lorsque il a besoin de modifier le modèle il utilisera invokeLater() pour le faire (ou invokeAndWait() s'il a besoin d'attendre la fin du traitement).

    Le plus simple serait de te faire une série de méthode qui effecuerons la tâche dans l'EDT, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        public void removeFromModel(final int index) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    monModel.remove(index);
                }
            });
        }
    a++
    Ok, je vais essayer de faire ça.
    Faut que je me replonge dans ces mécanismes...

    Merci!
    PS : je laisse le sujet ouvert au cas où...

  7. #7
    Membre éclairé
    Avatar de seiryujay
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    950
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 950
    Points : 722
    Points
    722
    Par défaut
    Me revoilà avec mon problème...

    Alors moi tout naïf que je suis, je m'étais dit : "temps de calcul important => on va mettre une progress bar et faire les traitements longs dans un thread".
    On me dit "modification du model de ma table => à faire dans l'EDT".

    Moi tout content, je me fais.
    Mais une fois que c'est fait, je me rend compte d'un truc : ce qui prend du temps, c'est la mise à jour du model!

    Je m'explique :
    Si je fais du undo après une suppression d'un noeud, je recrée le noeud supprimé, ainsi que TOUS SES FILS.
    Or dans le cas d'un grand nombre de fils, ça prend du temps. Et ça c'est de la modification du modeln donc à faire dans l'EDT, non?

    Je poste 2 ou 3 bouts de code qui expliquent le schmilblick :

    1) Méthode de rechargement des noeuds fils (présente dans le model) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    /**
     * Recharge les fils du noeud <code>node</code>.
     */
    public void reloadChildren(Object node) {
    	DefectNode dn = (DefectNode) node;
     
    	synchronized (this) {
    		reloadCount++;
    	}
    	dn.resetSize();
    	new DefectNodeLoader((DefectNode) node).load();
    }
    2) Constructeur de DefectNodeLoader appelé juste au-dessus :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    DefectNodeLoader(DefectNode node) {
    	this.node = node;
    	node.setChildren(node.createChildren(defectMS), true);
    	node.setTotalSizeValid(false);
    }
    3) Méthode createChildren() (qui crée les neuds fils) appelée juste au-dessus :
    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
     
    /**
     * Loads the children of the receiver.
     */
    protected DefectNode[] createChildren(MergeSort sorter) {
    	DefectNode[] retArray = null;
     
    	try {
    		Defect[] defects = defect.getSubDefectsArray();
    		if (defects != null) {
    			if (sorter != null) {
    				sorter.sort(defects);
    			}
    			retArray = new DefectNode[defects.length];
     
    			for (int i=0; i<defects.length; i++) {
    				Defect childDefect = defects[i];
    				retArray[i] = new DefectNode(this, childDefect);
    			}
    		}
    	} catch (SecurityException se) {
    		System.out.println("DefectTableModel : SecurityException - "+se.getMessage()); //$NON-NLS-1$
    	}
     
    	if (retArray == null) {
    		retArray = EMPTY_CHILDREN;
    	}
    	return retArray;
    }
    C'est ça qui prend du temps, je pense...

    4) Méthode load() appelée juste dans le 1) :
    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
     
    public void load() {
    	DefectNode[] children = node.getChildren();
     
    	sizeMS = getSizeSorter();
    	for (int counter = children.length - 1; counter >= 0; counter--) {
    		if (!children[counter].isLeaf()) {
    			reloadNode = children[counter];
    			loadChildren(children[counter]);
    			reloadNode = null;
    		}
    		if (!isValid) {
    			counter = 0;
    		}
    	}
    	recycleSorter(sizeMS);
    	if (isValid) {
    		MergeSort sorter = getSizeSorter();
    		sorter.sort(node.getChildren());
    		recycleSorter(sorter);
    		node.setChildren(node.getChildren(), true);
    		synchronized (DefectTableModel.this) {
    			reloadCount--;
    			DefectTableModel.this.notifyAll();
    		}
    	} else {
    		synchronized (DefectTableModel.this) {
    			reloadCount--;
    			DefectTableModel.this.notifyAll();
    		}
    	}
    }
    5) Méthode loadChildren() appelée juste au dessus :
    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
     
    protected void loadChildren(DefectNode node) {
    	if (!node.isLeaf()) {
    		final DefectNode[] children = node.createChildren(null);
     
    		for (int counter = children.length - 1; counter >= 0; counter--) {
    			if (!children[counter].isLeaf()) {
    				children[counter].forceTotalSizeValid();
    			}
    			if (!isValid) {
    				counter = 0;
    			}
    		}
    		if (isValid) {
    			final DefectNode dn = node;
     
    			// Reset the children
    			MergeSort sorter = getSizeSorter();
     
    			sorter.sort(children);
    			recycleSorter(sorter);
    			dn.setChildren(children, true);
    			dn.setTotalSizeValid(true);
    			dn.nodeChanged();
    		}
    	} else {
    		node.forceTotalSizeValid();
    	}
    }
    tout ce code a été récupéré dans l'exemple Sun (cf lien en début de topic).

    Et je ne vois pas ce que je peut faire pour que ce reloadChildren() ne bloque pas l'affichage... (à part le sortir de l'EDT, mais apparemment, ce n'est pas ce qu'il faut faire...)

    Une idée pour me débloquer?...

  8. #8
    Membre éclairé
    Avatar de seiryujay
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    950
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 950
    Points : 722
    Points
    722
    Par défaut
    J'ai fait un petit test en enlevant le reloadChildren() du invokeAndWait() (donc en le laissant dans un thread à part), et ça a l'air de marcher...
    Je vais faire quelques tests supplémentaires quand même...

  9. #9
    Membre éclairé
    Avatar de seiryujay
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    950
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 950
    Points : 722
    Points
    722
    Par défaut ListSelectionListener et JTreeTable
    Et me revoici!

    Donc maintenant ce problème est résolu, mais j'ai un nouveau souci qui est, me semble-t'il, toujours en rapport avec le précédent.

    En fait, les données que je stocke dans mon modèle sont représentées graphiquement à 2 endroits :
    1) dans la JTreeTable stockant les informations des mes formes et groupes de formes.
    2) sur une image où les groupes de formes sont représentés.

    J'ai un système qui me permet de mettre en surbrillance la forme sur l'image lorsque la ligne de la JTreeTabl correspondante est sélectionnée.
    Au départ, j'utilisais un KeyListener, un MouseListener et un MouseMotionListener pour détecter le changement de sélection.
    Mais j'ai découvert récemment le ListSelectionListener.

    Donc j'ai décidé de l'utiliser et de virer les autres listeners. Du coup, je fais ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    treeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
    	public void valueChanged(ListSelectionEvent arg0) {
        	    	/** On propage le changement de sélection. */
    	  	fireSelectionChangedEvent(treeTable.getSelectedDefects(), (KeyEvent) null);
    	}
    });
    avec
    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
     
    public void fireSelectionChangedEvent(ArrayList selectedDefects, KeyEvent ke) {
    	/** On propage l'évènement aux listeners pour mettre à jour l'image. */
    	for (int i=0; i<listListener.size(); i++) {
    		((DefectTableListener) listListener.get(i)).defectTableSelectionChanged(selectedDefects);
    	}
     
       	/** On propage l'évènement clavier sur le tableau
             * pour recentrer l'image sur la forme correspondante. */
    	TreeTableCellRenderer tree = (TreeTableCellRenderer) treeTable.getDefaultRenderer(TreeTableModel.class);
    	TreeSelectionModel treeSelectionModel = tree.getSelectionModel();
    		int treeRowsNumber = ((DefectNode) tree.getModel().getRoot()).getChildrenList().size();
     
    	DefaultListSelectionModel listSelectionModel = (DefaultListSelectionModel) treeTable.getSelectionModel();
    	int lead = listSelectionModel.getLeadSelectionIndex();
    	// J'ai dû rajouter ce test parce que sinon ça crachait...
    	if (lead <= treeRowsNumber) {
    		int selectedRow = lead;
    		if (selectedRow != -1) {
    			DefectNode dn = treeTable.nodeForRow(selectedRow);
    			if (dn != null)
    				fireDefectHighlightedEvent(dn.getDefect());
    		}	
    	}
    	else {
    		/* 
    		 * On a supprimé des défauts => il y a une légère incohérence entre tree et treeTable :
    		 * on a supprimé les noeuds, mais le ListSelectionModel de treeTable n'est pas tout à fait à jour
    		 * apparemment...
    		 */
    	}
    }
    Mais j'ai un souci lorsque par exemple je sélectionne toute ma JTreeTable et que j'appuies sur la touche "Suppr". Là ben forcément, j'essaie de supprimer toutes mes formes, mais ça crashe...

    En fait, j'ai l'arbre qui sert de renderer à ma JTreeTable qui est bien à jour (les noeuds relatifs aux formes supprimées ont bien disparu => dans le cas de notre exemple, y'a plus de noeuds...), mais la méthode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    listSelectionModel.getLeadSelectionIndex()
    me renvoie 13 (autrement dit le numéro de la dernière ligne sélectionnée).
    Donc il considère qu'il reste 13 ligne dans ma table, et la méthode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    treeTable.nodeForRow(selectedRow);
    crashe puisqu'elle cherche le 13ème noeud de l'arbre qui n'en compte plus aucun.

    Donc je dois oublier d'appeler une méthode de mise à jour de ma JTreeTable ou je fais quelque chose qu'il ne faut pas, mais je ne vois pas quoi...

    Quelqu'un aurait-il une idée?...

Discussions similaires

  1. Réponses: 6
    Dernier message: 31/12/2011, 07h03
  2. jointure sur plusieurs tables fonctionne pas
    Par jmsch dans le forum Requêtes
    Réponses: 1
    Dernier message: 03/11/2010, 21h55
  3. Réponses: 0
    Dernier message: 02/06/2009, 14h09
  4. Réponses: 4
    Dernier message: 06/08/2007, 16h17

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