In the example, I\'m seeking to add a table to my GUI and then dynamically add rows to it (to show the progress). What I don\'t understand is why all the rows are appearing
As pointed out by kleopatra and peeskillet, my initial example suffered from a stupid mistake. It's worth noting that peeskillet and I were following different approaches, though. In my example, the columns meant to represent connection attempts (more or less) that can take an unknown amount of time and that can actually fail (in that case, and only in that case, the next column would come into play and so on). Therefore, it wouldn't have made sense for me to add the rows at once (which was probably what made my example look weird to peeskillet). I've solved the task using a SwingWorker. As pointed out by kleopatra, there was a another mistake, which is now fixed. Here's my code:
package SwingWorkerExampleCopy;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import java.awt.BorderLayout;
public class SwingWorkerExampleCopy {
public static void main(String[] args) {
// Show GUI
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
GUI gui = new GUI();
DefaultTableModel tableModel = new DefaultTableModel();
// Use a SwingWorker
Worker worker = new Worker(tableModel);
worker.execute();
JTable table = new JTable(tableModel);
table.setEnabled(false);
// table.setTableHeader(null);
JScrollPane scrollPane = new JScrollPane(table);
gui.getContentPane()
.add(scrollPane, BorderLayout.CENTER);
}
});
}
}
class GUI extends JFrame {
private static final long serialVersionUID = 1L;
public GUI() {
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 350, 400);
setLocationRelativeTo(null);
setVisible(true);
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(10, 10, 10, 10));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
}
class Worker extends SwingWorker<DefaultTableModel, Object[]> {
private final static int numRows = 10;
private final static int numCols = 10;
private DefaultTableModel model;
Worker(DefaultTableModel model) {
this.model = model;
model.setColumnCount(numCols);
}
@Override
protected DefaultTableModel doInBackground() throws Exception {
// Add row
for (int row = 0; row < numRows; row++) {
// Build columns
for (int col = 0; col < numCols; col++) {
if (col == 0) {
publish(new Object[] { new String("Row " + row), row,
col });
} else {
// Simulate a slow source
Thread
.sleep(new Random().nextInt((250 - 50) + 1) + 50);
Boolean isSuccessful = false;
// Simulate a return value
if (new Random().nextBoolean()) {
isSuccessful = true;
}
publish(new Object[] {
new String((isSuccessful == true ? "x" : "o")), row,
col });
if (isSuccessful == true) {
break;
}
}
}
}
return model;
}
@Override
protected void process(List<Object[]> chunks) {
for (Object[] chunk : chunks) {
// chunk[0]: cell value
// chunk[1]: number
// chunk[2]: column
if ((int) chunk[2] == 0) {
Object[] row = new Object[numCols];
row[0] = (Object) chunk[0];
model.addRow(row);
} else {
model.setValueAt((Object) chunk[0], (int) chunk[1],
(int) chunk[2]);
}
}
}
}
Because while your code is running, no other events (such as repaint events) can execute - you're blocking the event thread until you're done.
You may be able to call repaint directly, but the UI will still be unresponsive to input while your code is running. You'd be better off running the loop in a separate worker thread, and using invokeLater or invokeAndWait to perform the updates to the UI when needed.
Reiterating Kleopatra : Don't Sleep the EDT
You can instead use a javax.swing.Timer
as seen in this answer
EDIT
I didn't want to mess with your code too much (just because it looks weird to me) but I changed it somewhat to add the Timer
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
public class Main {
static JTable table;
static GUI gui;
static Processor p = null;
public static void main(String[] args) {
// Show GUI
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
gui = new GUI();
p = new Processor() {
@Override
public void execute() {
final JTable table = new JTable(p.getTableModel());
final JScrollPane scrollPane = new JScrollPane(table);
gui.getContentPane().add(scrollPane, BorderLayout.CENTER);
gui.setLocationRelativeTo(null);
gui.setVisible(true);
Timer timer = new Timer(100, new ActionListener(){
public void actionPerformed(ActionEvent e) {
p.processRow();
table.scrollRectToVisible(table.getCellRect(table.getRowCount() - 1, 0, true));
}
});
timer.start();
}
};
p.execute();
}
});
}
}
class GUI extends JFrame {
private static final long serialVersionUID = 1L;
public GUI() {
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 350, 400);
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(10, 10, 10, 10));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
}
interface Callback {
void execute();
}
abstract class Processor implements Callback {
private final String[] cols = {"COL", "COL", "COL", "COL", "COL"};
private DefaultTableModel tableModel;
int numRows;
int numCols;
int a, b, c, d, e;
Processor() {
a = 1; b = 2; c = 3; d = 4; e = 4;
numRows = 1000;
tableModel = new DefaultTableModel(cols, numCols);
}
public DefaultTableModel getTableModel() {
return tableModel;
}
public void processRow() {
tableModel.addRow(new Object[]{a, b, c, d, e});
a++; b++; c++; d++; e++;
}
}