Search code examples
javaswingjtableswingworker

SwingWorker doesn't update JTable


I have a class of my SwingWorker where I want to update data in JTable from DB:

public class MySwingWorker extends SwingWorker<Void, Monitor> {


    public DBMonitor dbmonitor;  
    Vector<Vector> rowData;
    Vector<String> columnNames;
    DefaultTableModel model;
    Monitor obj;

    @Override
    protected Void doInBackground() throws Exception {

        dbmonitor = new DBMonitor();
        rowData = dbmonitor.getJobsData();
        columnNames = dbmonitor.getColumnNames();
        obj.setJTable(rowData, columnNames);

        while (!isCancelled()) {
            Thread.sleep(10000);
            JOptionPane.showMessageDialog(null, "SLEEP!");
            rowData = dbmonitor.getJobsData();
            columnNames = dbmonitor.getColumnNames();
            model = new DefaultTableModel(rowData, columnNames);
            publish(new Monitor(model));
        }
        return null;
    }

    @Override
    protected void process(List<Monitor> monitors) {
        Monitor monitor = monitors.get(monitors.size() - 1);
        monitor.updateJTable(monitor.getModel());
    }

}

And Monitor class where I render Tabs, JTable and execute SwingWorker:

public class Monitor extends JFrame{

    TableModel model;
    JTable jtable = null;
    JTabbedPane jtp = null;
    JPanel jp1 = null;
    JPanel jp2 = null;
    JLabel label1 = null;
    JLabel label2 = null;

    MySwingWorker worker;

    public void setJTable(Vector data, Vector columnNames) {

        jtable = new JTable(data, columnNames);
        JScrollPane scrollPane = new JScrollPane(jtable);
        jp1.add(scrollPane);
    }

    public void updateJTable(TableModel model) {
        jtable = new JTable(model);
    }

    public Monitor() throws ClassNotFoundException, SQLException {

        setTitle("Monitor System");
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        //Panel with tabs for navigation
        jtp = new JTabbedPane();
        getContentPane().add(jtp);

        //tab1, info from dba_jobs
        jp1 = new JPanel();

        //tab2 info from QueueInfo
        jp2 = new JPanel();
        label1 = new JLabel();
        label1.setText("tab1");

        label2 = new JLabel();
        label2.setText("tab2");

        jp1.add(label1);
        jp2.add(label2);

        jtp.add("Tab1", jp1);
        jtp.add("Tab2", jp2);

        DBMonitor dbmonitor = new DBMonitor();
        Vector<Vector> rowData = dbmonitor.getJobsData();
        Vector<String> columnNames = dbmonitor.getColumnNames();

        setJTable(rowData, columnNames);
        setSize(600, 600);
        setVisible(true);

        (worker = new MySwingWorker()).execute();
    }

    public Monitor(TableModel model) {
        this.model = model;
    }

    public TableModel getModel() {
        return model;
    }

}

And in my Main method I execute:

SwingUtilities.invokeLater(new Runnable() {

    @Override
    public void run() {
        new Monitor(); 
    }
});

The problem is that, showMessageDialog() doesn't append, and JTable doesn't refresh. I want update JTable from DB every 10 sec. The Example I take from here


Solution

  • Your JTable does not update because when publishing you create a new JFrame (called Monitor) and set the new TableModel to it. You never show the new JFrame, nor do you update the already existing one. Hence, of course, you won't see any updates.

    As for how to do this properly:

    • You need to give your SwingWorker either a callback or a reference to the already existing Monitor (explicit via a variable, or implicit by moving it into an inner class). Only then will you update the correct JFrame
    • Even then, you are creating a new JTable every time your model changes and not replacing your component. I.E. your frame will still show the old table and have the data for a new one (but will not show it). You need to either replace the table in the updateTable method, or (much better solution) update the model.

    EDIT

    An example of how this can look like (Assuming your column names don't change, if they do send them over just like the rows):

    public class Monitor extends JFrame{
    
        TableModel model;
        Vector<String> columnNames;
        <other components>
        public Monitor() throws SQLException {
            columnNames = dbmonitor.getColumnNames();
            model = new DefaultTableModel(columnNames, 0);
            <Add all components, including table with the created table model>
            (worker = new MySwingWorker()).execute();
        }
    
        private class MySwingWorker extends SwingWorker<Void, Vector<Vector>> {
             @Override
             protected Void doInBackground() throws Exception {
                 dbmonitor = new DBMonitor();
                 rowData = dbmonitor.getJobsData();
    
                 while (!isCancelled()) {
                     Thread.sleep(10000);
                     publish(dbmonitor.getJobsData());
                 }
                 return null;
              }
    
              @Override
              protected void process(List<Vector<Vector>> data) {
                  Vector<Vector> lastPublish = data.get(data.size() - 1);
                  tableModel.setDataVector(lastPublish, columnNames);
              }
        }
    }