简体   繁体   中英

Java update jtable row from another thread

I have a multi threaded program and I'm trying to use a JTable to view the progress of each thread. Each thread is a row the JTable. I have posted a simplified and relevant code of what I have now. Each time I press start a new thread is started and the row is added to the JTable. But how do I update the "Column 2" which of the row which belongs to that thread from the running thread?

In main this is what I have

JTable table = new JTable(new DefaultTableModel(new Object[]{"Thread Name", "Progress"}, 0));

btnBegin.addActionListener(new ActionListener()
{
  public void actionPerformed(ActionEvent arg0)
  {
    Thread newThread = new Thread(new MyThreadClass(country, category));
    newThread.start();
    DefaultTableModel model = (DefaultTableModel) table.getModel();
    model.addRow(new Object[]{"Thread " + threadNumber, "Column 2"});
  }
});

if you want to update the column at runtime and if you know in which row you want to update, you can use it this way:

int row; // row number you want to update.
int column = 1; // You want to update the first row.
DefaultTableModel model = (DefaultTableModel)table.getModel();
Object value = "New Value Of This Cell";
model.setValueAt(value, row, column);

You can pass or set your table, and the row in which the thread is showing to the MyThreadClass, so it can update its row by itself.

You can write a method insie MyThreadClass like this:

public void setTableAndRow(int row, JTable table) {
    this.table = table;
    this.row = row;
}

You can pass those arguments while creating the thread:

btnBegin.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        MyThreadClass myThread = new MyThreadClass(country, category);
        Thread newThread = new Thread(myThread);
        /**
         * We know, if there is n rows starting from 0,
         * index of the row that we're adding is n.
         */
        int row = model.getRowCount();
        myThread.setTableAndRow(row, table);
        // Now we add the row before starting the thread, because it
        // will try to reach that row, we don't want any exceptions.
        model.addRow(new Object[]{"Thread " + threadNumber, "Column 2"});
        newThread.start();
    }
});

If you can add a field tableModel to MyThreadClass (field+constructor argument), from this class, you can invoke the code @sedran was mentioning.

Example:

public class T {

static int threadNumber = 0;

public static void main(String[] args) throws Exception {

    final JTable table = new JTable(new DefaultTableModel(new Object[] { "Thread Name", "Progress" }, 0));

    JButton btnBegin = new JButton("Begin");
    btnBegin.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            DefaultTableModel model = (DefaultTableModel) table.getModel();
            MyThreadClass newThread = new MyThreadClass(model, threadNumber++);
            newThread.start();

            model.addRow(new Object[] { "Thread " + threadNumber, "Column 2" });
        }
    });

    JFrame frame = new JFrame();
    frame.add(btnBegin, BorderLayout.NORTH);
    frame.add(table);
    frame.setSize(400, 400);
    frame.setVisible(true);
}

static class MyThreadClass extends Thread {
    private final DefaultTableModel model;
    private final int threadNumber;

    public MyThreadClass(DefaultTableModel model, int threadNumber) {
        super();
        this.model = model;
        this.threadNumber = threadNumber;
    }

    @Override
    public void run() {
        for (int i = 0; i <= 5; i++) {
            final int index = i;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    model.setValueAt(index * 20, threadNumber, 1);
                }
            });
        }
    }
}

}

Please note that the use of SwingUtilities.invokeLater is important as the view MUST be refreshed in the EDT.

Suggestions:

  • Use a SwingWorker object to create your background thread.
  • One way to get updates from the SwingWorker is by using its publish/process method pair. This allows the background thread to pass data to the Swing application on the Swing event thread. This will allow the background process to "push" data onto the GUI.
  • The other way to get updates is to add a PropertyChangeListener to the SwingWorker and have the SwingWorker update bound properties. Either will work fine. This will allow data to be "pulled" from the background process onto the GUI.
  • To update the data in a specific data row, you'll need to somehow connect a row of the model with the thread you're following. You could always iterate through the cells of the a certain column of the table model using getValueAt(...) until you've found one that contains data that matches that of the thread, perhaps a field of the watched class. Then you can update the data held by a different column of that row using JTable's setValueAt(...)

我认为您正在寻找SwingUtilies.invokeLater() ,因此您可以访问事件调度线程

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM