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

Composants Java Discussion :

Erreur de thread sur JTable triée


Sujet :

Composants Java

  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 821
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 821
    Points : 979
    Points
    979
    Par défaut Erreur de thread sur JTable triée
    Bonjour,

    J'ai un problème de fonctionnement sur une JTable qui à la fonction de trie activée

    classe MainFrame (fenêtre principale) :
    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
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.table.DefaultTableModel;
    
    
    public class MainFrame extends JFrame {
    
        private static Thread t;
        private Discovery objDiscovery;
    
        // ****************************************************
        private final JPanel sJPanel1 = new JPanel();
        private final JPanel ssJPanel1 = new JPanel();
        private final JPanel ssJPanel2 = new JPanel();
    
        private JTable jTable1;
        private MyTableModel2 jTableModel1;
        private final JButton jButton1 = new JButton("Test");
        MainFrame myFrame = this;
    
    
        // constructeur
        public MainFrame(){
            super();      
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setResizable(true);
            initComposant(); // construction de la fenetre
            this.setVisible(true);
            this.setSize(600, 400);
            run_app(); // lancement des Threads
        }
    
    
        // efface le tableau
        private void clearTable(){
            while (jTableModel1.getRowCount() != 0){
                jTableModel1.removeRow(jTableModel1.getRowCount() - 1);
            }
        }
    
    
    
        // construction de la fenetre
        private void run_app(){
    
            // **********************************************************************
            objDiscovery = new Discovery();
    
            objDiscovery.addStatusListener(new StatusListener(){
                @Override
                public void infoMessageDetected(String[] messages, short type){
    
                    System.out.print("Discovery.listener");
    
                    System.out.print("Discovery.listener.add");                    
                    for (String message : messages) {
                        System.out.print(" " + message);
                    }
    
                    //jTable1.setAutoCreateRowSorter(false);
                    jTableModel1.addRow(messages); // ajout de la ligne
                    //jTable1.setAutoCreateRowSorter(true);
                }
    
            });
    
            // bouton clear console
            jButton1.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    clearTable();
                    objDiscovery.sendDiscoveryMsg();
               } 
            }); 
    
    
            t = new Thread(objDiscovery);
            t.start();
    
            objDiscovery.sendDiscoveryMsg(); // lance une requete au demarrage
        }
    
    
    
        // construction de la fenetre
        private void initComposant(){
            String[] columnNames = {"column 1", "column 2", "column 3", "column 4", "column 5", "column 6"};
            Object[][] data = null;
    
            jTable1 = new JTable(new MyTableModel2(data, columnNames));
            jTableModel1 = (MyTableModel2)jTable1.getModel(); // pour pouvoir utiliser l'ajout/suppression de lignes
            JScrollPane jScrollPane1 = new JScrollPane(jTable1);
            jTable1.setFillsViewportHeight(true);
            jTable1.setAutoCreateRowSorter(true); // pour activer la gestion de trie
            this.setLayout(new BorderLayout());
            this.getContentPane().add(sJPanel1, BorderLayout.NORTH);
            this.getContentPane().add(jScrollPane1, BorderLayout.CENTER);
            sJPanel1.setLayout(new BorderLayout());
            sJPanel1.add(ssJPanel1, BorderLayout.WEST);
            sJPanel1.add(ssJPanel2, BorderLayout.EAST);
            ssJPanel1.add(jButton1);
        }
    
        // initialise la Table suivant le modele
        private class MyTableModel2 extends DefaultTableModel {
            public MyTableModel2(Object[][] data, String[] columnNames) {
                super(data, columnNames);
            }
    
            @Override
            public boolean isCellEditable(int row, int col) {
                return false;
            }	
        }	
    	
    }

    classe Discovery qui envoie des infos vers la JTable pour la remplir
    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
     
    import javax.swing.event.EventListenerList;
     
     
    public final class Discovery implements Runnable {
     
        private boolean stopThread = false; // mettre volatile pour le multi-threading
        Integer counter = 9;
        boolean sendMessage = false;
     
        // constructeur
        public Discovery(){
            super();
        }
     
        // envoie le message
        public void sendDiscoveryMsg(){
            sendMessage = true;
        }
     
        // reception des data
        // lance le thread
        @Override
        public void run(){
            boolean threadOff = false;
            while(!threadOff){
                if(sendMessage == true){
                    System.out.println("run.start");
                    String[] myStr = {"a", "b", "c", "d", "e", "f"};
     
                    myStr[0] = counter.toString(); 
                    switch(counter){
                        case 9:
                            counter = 5;
                            break;
     
                        case 5:
                            counter = 2;
                            break;     
     
                        case 2:
                            counter = 7;
                            break;
     
                        default:
                            counter = 9;
                            sendMessage = false;
                            break;
                    }
     
     
                    sendMessage(myStr);                
                }
     
                synchronized(this) {
                    threadOff = this.stopThread;
                }
            }
        }
     
     
        // ferme le thread
        public void close(){
            this.stopThread = true;
            System.out.println("Discovery : close");
        }
     
     
        // ***********************************************************************
        // Gestion des messages console (implementation de l'interface StatusListener que j'ai créée)
        private final static EventListenerList listeners = new EventListenerList();
     
    	// gestion de l'ajout d'un ecouteur a liste listeners
    	// => on peut ajouter plusieurs type de listener dans la liste (on repere leur type grace a  StatusListener.class)
        public void addStatusListener(StatusListener listener){
            listeners.add(StatusListener.class, listener);
        }
     
        // suppression d'un listener dans la liste
        public void removeStatusListener(StatusListener listener){
            listeners.remove(StatusListener.class, listener);
        }
     
        // recupere la liste de tous les abonnés
        public StatusListener[] getStatusListener(){
            return listeners.getListeners(StatusListener.class);
        }
     
        // envoie de l'evenement
        //protected void sendMessage(String message, short type){
        protected void sendMessage(String[] messages){			
            System.out.print("DiscoveryMsg : send => " );
     
            for(int i = 0; i<messages.length; i++){
                    if (i !=0){
                        System.out.print(" / ");
                    }
                    System.out.print(messages[i]);											
            }
            System.out.println(); // fin de la ligne
     
            for(StatusListener listener : getStatusListener()) {
                listener.infoMessageDetected(messages, (short) 0);
            }    
        }
     
    }
    interface StatusListener pour la gestion d’événements
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    import java.util.EventListener;
     
    public interface StatusListener extends EventListener{
        public void infoMessageDetected(String[] messages, short type);
    }

    Lorsqu'on spam le bouton "test", tout fonctionne bien.
    Mais si on appuie sur l’entête de la première colonne pour activer le tri et qu'ensuite on spam le bouton "test", on peut voir dans la console qu'il y a l'erreur suivante qui est générée :
    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at javax.swing.DefaultRowSorter.convertRowIndexToModel(DefaultRowSorter.java:518)
    at javax.swing.JTable.convertRowIndexToModel(JTable.java:2642)
    at javax.swing.JTable.getValueAt(JTable.java:2717)
    at javax.swing.JTable.prepareRenderer(JTable.java:5719)
    at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2114)
    at javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:2016)
    at javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1812)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at javax.swing.JComponent.paintComponent(JComponent.java:778)
    at javax.swing.JComponent.paint(JComponent.java:1054)
    at javax.swing.JComponent.paintToOffscreen(JComponent.java:5219)
    at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1529)
    at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1452)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1249)
    at javax.swing.JComponent._paintImmediately(JComponent.java:5167)
    at javax.swing.JComponent.paintImmediately(JComponent.java:4978)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:808)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:796)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:769)
    at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:718)
    at javax.swing.RepaintManager.access$1100(RepaintManager.java:62)
    at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1677)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
    at java.awt.EventQueue.access$200(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:694)
    at java.awt.EventQueue$3.run(EventQueue.java:692)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

    Remarque : dans la classe MainFrame, si j'active les deux lignes qui sont en commentaire (voir ci-dessous), il n'y a plus d'erreur généré mais le tri ne fonctionne plus.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
                    //jTable1.setAutoCreateRowSorter(false);
                    jTableModel1.addRow(messages); // ajout de la ligne
                    //jTable1.setAutoCreateRowSorter(true);

    Comment résoudre le problème, je ne trouve vraiment pas la solution ?

    Merci d'avance

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    Et si tu appelais jTableModel1.addRow(messages); sur l'EDT (avec un SwingUtilities.invokeLater()) ? A mon avis le problème vient de la concurrence d'exécution entre ton thread et l'EDT (le thread de SWING) : le viewModel du rowSorter a commencé à être modifié mais pas complètement au moment du repaint et patatras.

    Et supprime le truc avec les setAutoCreateRowSorter(false)/setAutoCreateRowSorter(true).
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  3. #3
    Expert éminent
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Points : 7 752
    Points
    7 752
    Par défaut
    Hello,
    Quand tu appelles sendMessage depuis ta classe Discovery, ça appelle les listeners puis par conséquent infoMessageDetected à la ligne 57.
    Le problème, c'est que les listeners sont de simples méthodes comme les autres qui respectent une interface, et donc il s'exécutent dans le même Thread qui a appelé sendMessage. Ce n'est pas parce que le code se trouve dans MainFrame que celui-ci va être appelé dans le Thread UI.

    Pour info, et c'est valable pour à peu près tous les langages de programmation et librairies graphiques, lorsqu'il est question d'interfaces graphiques, il y a une notion de Thread UI, cela désigne le Thread qui gère l'affichage et la mise à jour des composants, dispatche les évènements etc... Si un autre Thread (ici celui que tu lances au début qui exécute la méthode run() de discovery) commence à trafiquer les sources de données pendant que le Thread UI bosse, il y a grand risque que ça finisse mal.
    La règle par conséquent, c'est que les modifications qui ont un impact sur l'affichage (comme ici l'ajout de ligne dans le TableModel) doivent en principe toujours être faites dans le Thread UI, et surtout pas dans un Thread qui bosse en arrière-plan.

    Comme suggéré plus haut, une possibilité est par exemple d'utiliser invokeLater() pour mettre à jour ton modèle. L'autre serait d'utiliser SwingWorker.

  4. #4
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 821
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 821
    Points : 979
    Points
    979
    Par défaut
    ok merci pour la réponse.

    Je dois donc juste modifier l'appel à la fonction infoMessageDetected comme cela (je ne connais pas bien le fonctionnement de l'EDT) ?
    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
     
            objDiscovery.addStatusListener(new StatusListener(){
                @Override
                public void infoMessageDetected(final String[] messages, short type){
     
                    System.out.print("Discovery.listener");
     
                    System.out.print("Discovery.listener.add");                    
                    for (String message : messages) {
                        System.out.print(" " + message);
                    }
     
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            jTableModel1.addRow(messages); // ajout de la ligne
                        }
                    });
     
                }
     
            });
    => ça semble fonctionner

  5. #5
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Oui, invokeLater permet d'exécuter du code sur le thread de SWING, sans attendre que cette exécution soit effective (later = plus tard, sous entendu "quand tu veux").

    La solution invoquée par _sKip à base de SwingWorker, un décorateur de thread dédié aux opérations longues à effectuer hors UI, mais qui agissent sur l'UI, permet de gérer de façon plus clean et plus simple ces aspects : ce qu'on fait hors du thread graphique pour ne pas bloquer l'affichage et l'interactivité, ce qu'on fait dans le thread graphique parce que qu'il faut le faire dans ce thread (mise à jour de l'affichage et réaction aux actions utilisateurs (interaction)).
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 821
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 821
    Points : 979
    Points
    979
    Par défaut
    Merci pour vos réponse.

    Le programme étant petit et quasiment terminé, je ne vais pas m’embêter avec un SwingWorker pour le moment

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

Discussions similaires

  1. Listener sur JTable
    Par calypso dans le forum Composants
    Réponses: 3
    Dernier message: 05/02/2009, 16h27
  2. Erreur Thread sur Appel dans DLL
    Par Danyel dans le forum VB.NET
    Réponses: 10
    Dernier message: 27/10/2008, 23h57
  3. [langage] Erreur d'arrondi sur petits nombres
    Par Tchetch dans le forum Langage
    Réponses: 7
    Dernier message: 12/01/2005, 10h11
  4. Erreur Pilote ODBC sur pages ASP
    Par zouritte dans le forum ASP
    Réponses: 2
    Dernier message: 12/12/2004, 13h42
  5. Erreur ORA-01036 sur un XMLGRAM
    Par sch dans le forum XMLRAD
    Réponses: 5
    Dernier message: 07/09/2004, 14h56

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