Java update jtable row from another thread - java

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 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.

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();
}
});

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(...)

I think SwingUtilies.invokeLater() is what you are looking for, so you can access event dispatch thread.

Related

How to refresh JTable after inserting data to database?

I'm populating JTable from access database. when running the code for the first time, table loads perfectly. Then adding new records to database from JDialog. What I was trying to do is to call loadData() method when JDialog is closed, but table is not updating.
This is my loadData() method:
private void loadData() {
System.out.println("sssss");
final String [] columnNames={"Seq", "First Name", "Last Name","Num1","Num2","Num3"};
connectDb();
data = new Object[rows][columns];
int row = 0;
try {
while(rs.next()){
for(int col = 0 ; col<columns; col++ ){
if(col==0)
data[row][col]=rs.getString("contact_seq");
if(col==1)
data[row][col]=rs.getString("contact_fname");
if(col==2)
data[row][col]=rs.getString("contact_lname");
if(col==3)
data[row][col]=rs.getString("contact_num1");
if(col==4)
data[row][col]=rs.getString("contact_num2");
if(col==5)
data[row][col]=rs.getString("contact_num3");
}
row++;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
model = new DefaultTableModel(data, columnNames){
/**
*
*/
private static final long serialVersionUID = 1L;
public boolean isCellEditable(int row, int column)
{
return false;
}
};
table = new JTable(model);
}`
this how I call loadData method when closing the JDialog.
JMenuItem mntmNew = new JMenuItem("New Contact");
mntmNew.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addData gui = new addData(viewData.this,rs);
gui.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
gui.setVisible(true);
gui.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e){
loadData();
}
});
}
});
mnFile.add(mntmNew);
My database is updated when adding the records but Jtable is not refreshed.
Here:
private void loadData() {
...
table = new JTable(model); // don't re-create the table here
}
Don't re-create the table but update its model instead, either by setting a new table model or by clearing and re-filling the current one:
private void loadData() {
...
table.setModel(model);
// Of course, table should have been initialized
// and placed first, and only once!
}
See examples here (includes SwingWorker to make database calls in a background thread), here and here. Please have a look to those answers, there are explanations to make the point clear.
This worked for me:
if (model.getRowCount() > 0) {
for (int i = model.getRowCount() - 1; i > -1; i--) {
model.removeRow(i);
}
}
setTablevalue();
I removed all the rows from the JTable and again called the setTableValue method to re-populate the table.
This is a shot in the dark, but maybe this will work?:
public void windowClosed(WindowEvent e) {
loadData();
// Add this line:
table.repaint();
}
If I understand what is going on, the underlying database is getting updated but the JTable component is not showing the updates. My guess is that you just have to call the repaint() method so that the JTable gets updated as well.

synchronize view of two JTable

I have two JTables one in main viewport and one in footer viewport, using JideScrollPane.
the problem is when the main JTable's view is customized, the footer JTable remains the
same, is there any way to synchronize their view?
thanks.
EDIT:
Here's a demo that will synch up the resizing of two tables that have similar columns. The idea is:
Create a custom TableColumnModelListener for each table's column model.
Upon resize, sync up the column widths. You'll have to disable the other listener temporarily, while this is happening.
For moving of columns, implement that logic in columnMoved(...) [left as an exercise]
This shows two-way synching:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class JTableResizeColumnsDemo implements Runnable
{
JTable table1, table2;
TableColumnModelListener columnListener1, columnListener2;
Map<JTable, TableColumnModelListener> map;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new JTableResizeColumnsDemo());
}
public void run()
{
Vector<String> names = new Vector<String>();
names.add("One");
names.add("Two");
names.add("Three");
table1 = new JTable(null, names);
table2 = new JTable(null, names);
columnListener1 = new ColumnChangeListener(table1, table2);
columnListener2 = new ColumnChangeListener(table2, table1);
table1.getColumnModel().addColumnModelListener(columnListener1);
table2.getColumnModel().addColumnModelListener(columnListener2);
map = new HashMap<JTable, TableColumnModelListener>();
map.put(table1, columnListener1);
map.put(table2, columnListener2);
JPanel p = new JPanel(new GridLayout(2,1));
p.add(new JScrollPane(table1));
p.add(new JScrollPane(table2));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(p);
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class ColumnChangeListener implements TableColumnModelListener
{
JTable sourceTable;
JTable targetTable;
public ColumnChangeListener(JTable source, JTable target)
{
this.sourceTable = source;
this.targetTable = target;
}
public void columnAdded(TableColumnModelEvent e) {}
public void columnSelectionChanged(ListSelectionEvent e) {}
public void columnRemoved(TableColumnModelEvent e) {}
public void columnMoved(TableColumnModelEvent e) {}
public void columnMarginChanged(ChangeEvent e)
{
TableColumnModel sourceModel = sourceTable.getColumnModel();
TableColumnModel targetModel = targetTable.getColumnModel();
TableColumnModelListener listener = map.get(targetTable);
targetModel.removeColumnModelListener(listener);
for (int i = 0; i < sourceModel.getColumnCount(); i++)
{
targetModel.getColumn(i).setPreferredWidth(sourceModel.getColumn(i).getWidth());
}
targetModel.addColumnModelListener(listener);
}
}
}
You can apply an Observer pattern: the first JTable observes the second and vice versa. Then you add listners to both tables so that, when one is "customized", the other is notified. Basically, "being notified" consists in a method invocation that causes the update of the JTable.
In order to do that, you have two options:
You define a class Observer with a "register" method and a
"notify" method. When creating a JTable, you register it with the
Observer. Then, the listener you create and associate to each JTable
invoke the "notify" method of the observer, which informs all other
registered JTables of the change
You define a sort of "callback method" notify in the class that contains and declares the JTable. This "notify" method is invoked within the listner and updates the correct JTable. You can also create two methods: one for updating one JTable and one for the other JTable
Usually this is done by using the same model for different ui components. Sadly the JTable contains a bug that will cause problems when sharing the TableColumnModel.
But you can work around it using this JTable
class ShareableColumnModelTable extends JTable {
/**
* Fixes http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4816146 and
* more...
*
*/
#Override
public void columnMarginChanged(ChangeEvent e) {
if (isEditing()) {
removeEditor();
}
TableColumn resizingColumn = null;
if (tableHeader != null) {
resizingColumn = tableHeader.getResizingColumn();
}
if (resizingColumn != null) {
if (autoResizeMode == AUTO_RESIZE_OFF) {
resizingColumn.setPreferredWidth(resizingColumn.getWidth());
} else { // this else block is missing in jdk1.4 as compared to
// 1.3
TableColumnModel columnModel = getColumnModel();
/**
* Temporarily disconnects this column listener to prevent
* stackoverflows if the column model is shared between
* multiple JTables.
*/
columnModel.removeColumnModelListener(this);
try {
doLayout();
} finally {
columnModel.addColumnModelListener(this);
}
repaint();
return;
}
}
resizeAndRepaint();
}
}
With the ShareableColumnModelTableshowed above you can share one column model bettween multiple tables.
public static void main(String[] args) {
JFrame frame = new JFrame("Column Sync");
Container contentPane = frame.getContentPane();
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
splitPane.setResizeWeight(0.5d);
contentPane.add(splitPane);
JTable table1 = new ShareableColumnModelTable();
JTable table2 = new ShareableColumnModelTable();
TableColumnModel tableColumnModel = createTableColumnModel();
table1.setModel(createTableModel1());
table2.setModel(createTableModel2());
table1.setColumnModel(tableColumnModel);
table2.setColumnModel(tableColumnModel);
splitPane.setLeftComponent(new JScrollPane(table1));
splitPane.setRightComponent(new JScrollPane(table2));
showFrame(frame);
}
private static TableColumnModel createTableColumnModel() {
TableColumnModel tableColumnModel = new DefaultTableColumnModel();
TableColumn column1 = new TableColumn(0);
column1.setHeaderValue("1. column");
tableColumnModel.addColumn(column1);
TableColumn column2 = new TableColumn(1);
column2.setHeaderValue("2. column");
tableColumnModel.addColumn(column2);
return tableColumnModel;
}
private static TableModel createTableModel1() {
DefaultTableModel tableModel = new DefaultTableModel();
tableModel.setColumnCount(2);
tableModel.addRow(new Object[] { "a", "b" });
return tableModel;
}
private static TableModel createTableModel2() {
DefaultTableModel tableModel = new DefaultTableModel();
tableModel.setColumnCount(2);
tableModel.addRow(new Object[] { "c", "d" });
return tableModel;
}
private static void showFrame(JFrame frame) {
frame.setSize(240, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}

Update JTable in SwingWorker

I have a JTable which populates data from DB. I want to refresh data in JTable every 10 minutes (for testing 10 sec is enough). I tried do it with a Thread, but I found that it is not good idea, and I need to use SwingWorker
public class Monitor extends JFrame{
JTable jtable = null;
JTabbedPane jtp = null;
JPanel jp1 = null;
JPanel jp2 = null;
JLabel label1 = null;
JLabel label2 = null;
public void setJTable(Vector data, Vector columnNames) {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jtable = new JTable(data, columnNames);
JScrollPane scrollPane = new JScrollPane(jtable);
jp1.add(scrollPane);
}
public void updateJTable(Vector data, Vector columnNames) {
jtable = new JTable(data, columnNames);
}
public Monitor() {
setTitle("Monitor System");
//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);
}
}
And my Demo class:
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Statement stmt = null;
Connection conn = null;
ResultSet rs = null;
Vector<String> columnNames = new Vector<String>();
Vector<Vector> rowData = new Vector<Vector>();
DBMonitor dbmonitor = new DBMonitor();
Monitor monitor = new Monitor();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
rowData = dbmonitor.getJobsData();
columnNames = dbmonitor.getColumnNames();
monitor.setJTable(rowData, columnNames);
monitor.setSize((int) dim.getWidth(), (int) dim.getHeight());
monitor.setVisible(true);
boolean interrupt = true;
while (interrupt) {
try {
rowData = dbmonitor.getJobsData();
columnNames = dbmonitor.getColumnNames();
monitor.updateJTable(rowData, columnNames);
try {
Thread.sleep(10000);
} catch (InterruptedException ie) {
return;
}
JOptionPane.showMessageDialog(null, "SLEEP!");
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage());
System.err.println(e.getMessage());
}
}
}
}
How I can do it with SwingWorker? I don't get a concept of that way.
In the doInBackground() method of the SwingWorker you have you while loop that:
retrieves the data from the database
creates your TableModel
use the publish() method of the SwingWorker to pass the TableModel to the 'process()` method of your SwingWorker
sleeps for 10 seconds
Then in the process() method of the SwingWorker you:
use the TableModel that was passed to the process() method to update your JTable.
Read the section from the Swing tutorial on Concurrency for more information and a working example or search the forum for more SwingWorker examples.
First, I would start with a Timer of some sort, I'm going to use a Swing Timer as it easy, but you could use a java.util.Timer instead...
private Timer updateTimer;
//...
updateTimer = new Time(10000, new ActionListener() {
public void actionListener(ActionEvent e) {
}
});
updateTimer.setRepeats(false);
updateTimer.start();
This allows you to be notified in about 10 seconds time...
Then you need a SwingWorker that can do the actual work...
public class UpdateWorker extends SwingWorker<TableModel, Void> {
private Monitor monitor;
private Timer updateTimer;
public UpdateWorker(Monitor monitor, Timer updateTimer) {
this.monitor = monitor;
this.updateTimer = updateTimer;
}
#Override
protected TableModel doInBackground() throws Exception {
Vector<Vector> rowData = dbmonitor.getJobsData();
Vector columnNames = dbmonitor.getColumnNames();
DefaultTableModel model = new DefaultTableModel(rowData, columnNames);
return model;
}
#Override
protected void done() {
try {
TableModel model = get();
monitor.updateTable(model);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
updateTimer.restart();
}
}
Now in the actionPerformed method of the ActionListener assigned to the timer, you would do something like..
public void actionListener(ActionEvent e) {
UpdateWorker worker = new UpdateWorker(monitor, this);
worker.execute();
}
To execute the worker. The reason for having a non-repeating timer is to ensure that the next update is set for n seconds from when the update completes, so you don't get overlapping updates
Oh, and this will require to update your Monitor to accept a TableModel rather then the Vectors you are using to create one, it's just simpler that way...

How to update the view of JTable after adding a new row?

This is my TableModel, I have extended AbstractTableModel
class CustomTableModel extends AbstractTableModel
{
String[] columnNames = {"Name","Contact","eMail","Address","City","Pin","State","Type","ID"};
Vector<String[]> data = new Vector<String[]>();
CustomTableModel()
{
try
{
//Using JDBC connection//
while(rs.next())
{
String[] s=new String[9];
s[0]=rs.getString(1);
//System.out.println(s[0]);
s[1]=rs.getString(2);
s[2]=rs.getString(3);
s[3]=rs.getString(4);
s[4]=rs.getString(5);
s[5]=rs.getString(6);
s[6]=rs.getString(7);
s[7]=rs.getString(8);
s[8]=rs.getString(9);
data.add(s);
}
}
catch(Exception e)
{
System.out.println("the exception is :"+e.toString());
}
}
public int getColumnCount() {
int columnCount = columnNames.length;
return columnCount;
}
public int getRowCount() {
int rowCount = data.size();
return rowCount;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex)[columnIndex];
}
public String getColumnName(int column) {
return columnNames[column];
}
public void removeRow(int r)
{
for(int i=0;i<data.size();i++)
{
String[] s = (String[])data.get(i);
if(s[0]==getValueAt(r,0))
{
try
{
//using JDBC connections to delete the data from DB//
//also removing the value from data and also updating the view//
data.remove(data.get(i));
fireTableRowsDeleted(r, r);
}
catch (Exception e)
{
System.out.println(e.toString());
}
break;
}
}
}
//I am using the following code to update the view but it doesnot work//
public void addRow(String[] a)
{
data.add(a);
fireTableRowsInserted(data.size() - 1, data.size() - 1);
}
}
I have a table class which extends CustomTableModel .
class table extends CustomTableModel
{
final JButton editButton = new JButton("Edit");
final JButton deleteButton = new JButton("Delete");
final JTable mytable = new JTable(new CustomTableModel());
.
.
.
}
I have a add button , and in its action listener i use the following code to pass the values that i wanted to add.
String[] a = {"a","b","c","d","e","f","g","h","i"};
table myTableObj = new table();
myTableObj.addRow(a);
Pls let me know where i am going wrong . Thanks
Pls let me know where i am going wrong . Thanks
String[] a = {"a","b","c","d","e","f","g","h","i"};
table myTableObj = new table();
myTableObj.addRow(a);
code lines talking about
create a new row
create a new JTable
row is added to a new JTable
result is that a new JTable is never added to visible Swing GUI
don't do that, why is a new JTable recreated on every JButtons event
add String[] a... to the CustomTableModel directly
for better help sooner post an SSCCE, short, runnable, compilable
The table class makes no sense. It is supposed to be a TableModel that shoud be set into a JTable. Instead you have JTable declared as a field inside this table class (which should be Table btw according to Java naming convention). The result is that when constructing a new table object, a JTable is constructed inside it with another CustomTableModel inside. So the tableModel you are adding rows into is not the tableModel actually used by your JTable.
You can also use the myCustomTable.fireTableStructureChanged();

Setting the height of a row in a JTable in java

I have been searching for a solution to be able to increase the height of a row in a JTable. I have been using the setRowHeight(int int) method which compiles and runs OK, but no row[s] have been increased. When I use the getRowHeight(int) method of the row I set the height to, it does print out the size I increased the row to, so I'm not sure what is wrong. The code below is a rough illustration how I am trying to solve it.
My class extends JFrame.
String[] columnNames = {"Column 1", "Column 2", "Column 1 3"};
JTable table = new JTable(new DefaultTableModel(columnNames, people.size()));
DefaultTableModel model = (DefaultTableModel) table.getModel();
int count =1;
for(Person p: people)
{
model.insertRow(count,(new Object[]{count, p.getName(), p.getAge()+"",
p.getNationality}));
count++;
}
table.setRowHeight(1, 15);//Try set height to 15 (I've tried higher)
Can anyone tell me where I am going wrong? I am trying to increase the height of row 1 to 15 pixels?
You can use:
table.setRowHeight(int par1);
or if you wanted to set the row height for a specific row, use:
table.setRowHeight(int par1, int par2);
Not sure what is the intention of leaving the first row at index 0 empty. Rows in JTable run from index 0. It is best if you could post a complete example (ie SSCCE) that demonstrates the issues. Compare to this simple example that works OK:
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class DemoTable {
private static void createAndShowGUI() {
JFrame frame = new JFrame("DemoTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel();
model.setColumnIdentifiers(new Object[] {
"Column 1", "Column 2", "Column 3" });
JTable table = new JTable(model);
for (int count = 0; count < 3; count++){
model.insertRow(count, new Object[] { count, "name", "age"});
}
table.setRowHeight(1, 30);
frame.add(new JScrollPane(table));
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Right click on the JTable in JFrame and click Properties.
Scroll down and set the rowHeight value.
You can also add a tableModelListener?
model.addTableModelListener(new TableModelListener() {
#Override public void tableChanged(final TableModelEvent e) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
table.setRowHeight(e.getFirstRow(), 15); //replace 15 with your own height
}
});
}
});

Categories