In my Java 8 app I use a SwingWorker to get some data from a database and call publish once I know the data for a full row that I then want to add to my table:
DefaultTableModel dtm123 = (DefaultTableModel) myTable.getModel();
#Override
protected void process(List<Integer> chunks) {
if(chunks!=null) {
ListIterator<Integer> li = chunks.listIterator();
while(li.hasNext()) {
dtm123.addRow(myDataList.get(li.next()).getRowArray());
}
}
}
dtm123.getRowCount() is increased every time (I checked) but the table isn't updated to actually show the new rows. I tried to update it manually, which didn't work either:
dtm123.fireTableRowsInserted(0, dtm123.getColumnCount()-1);
Here's the weird thing: If I use the full version, it's filling the table just fine:
((DefaultTableModel) myTable.getModel()).addRow(myDataList.get(li.next()).getRowArray());
Can you not use the shorter version for filling a table? dtm123 isn't used anywhere else, so what's the difference between the two versions?
Edit:
Filling it directly like this (without the SwingWorker) works:
myTable.setModel(new DefaultTableModel(
new Object[][] {},
new Object[] {"blaA","blaB","blaC"}
));
DefaultTableModel dtm123 = (DefaultTableModel) myTable.getModel();
dtm123.addRow(new String[] {"bla1A","bla1B","bla1C"});
dtm123.addRow(new String[] {"bla2A","bla2B","bla2C"});
dtm123.addRow(new String[] {"bla3A","bla3B","bla3C"});
dtm123.addRow(new String[] {"bla4A","bla4B","bla4C"});
dtm123.addRow(new String[] {"bla5A","bla5B","bla5C"});
dtm123.addRow(new String[] {"bla6A","bla6B","bla6C"});
dtm123.addRow(new String[] {"bla7A","bla7B","bla7C"});
Looks like the SwingWoker is the problem but why? process is running on the main thread, which has to be used for UI updates, and there are no exceptions or error messages.
Fixed it - there were two reasons why it didn't work:
Setting the model of a table (myTable.setModel(new DefaultTableModel(...add info here...));) and creating the columns has to be done on the main/UI thread too.
process is called at a seemingly random point in time after calling publish() and there's no way to tell beforehand how many chunks there will be (could be just one or all of them), so you have to make sure that you get the DefaultTableModel after you set up the columns. To guarantee this, either set up the columns before you call publish() the first time (but still outside doInBackground - see 1.) or call publish for a "zeroth" time to set them up (see code below). In hindsight this seems pretty obvious...
If you're using a SwingWorker like me, you can do something along the lines of:
DefaultTableModel dtm123;
#Override
protected void process(List<Integer> chunks) {
if(chunks!=null) {
ListIterator<Integer> li = chunks.listIterator();
int next;
while(li.hasNext()) {
next = li.next();
if(next==-1) {
//Set up columns/column titles here
myTable.setModel(new DefaultTableModel(
new Object[][] {},
columnHeaderArray
));
dtm123 = (DefaultTableModel) myTable.getModel();
} else {
//Add new rows here
dtm123.addRow(myDataList.get(next).getRowArray());
}
}
}
}
Use publish as usual but once you know the columns' titles, call publish(-1) to set them up (of course, before you add the rows).
Related
I created an object type arrayList but table still not identifying it as an object and giving error
this is the error it is producing.
no suitable method found for add (object[])
Below is the code is used:
ArrayList<Object> CartItems = new ArrayList<Object>();
cartCheckout.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Cartgui.setVisible(true);
for (int i = 0; i < candyList.size(); i++) {
String name = candyList.get(i).getCandyName();
int weight = candyList.get(i).getweight();
int priceLbs = candyList.get(i).getPriceLbs();
Object[] data = {name, weight, priceLbs};
Cartgui.add(data);//this line is producing error
}
}
});
I expect the output to see name,weight and pricelbs in tabular form in table.
cartgui is the tablename
The myTable.add(something) method doesn't do what you think. You wish you could do something more like myTable.getTableModel().addData(something), which is conceptually correct, though no such addData() method exists.
You may want to do something like
TableModel myModel = new DefaultTableModel(something);
JTable myTable = new JTable(myModel);
where there are several options for something because DefaultTableModel has several constructors.
However I personally almost never use DefaultTableModel. Instead I do something like
class MyTableModel extends AbstractTableModel {
//override getRowCount(), getColumnCount(), getColumnName(), getValueAt(), and possibly isCellEditable() and setValueAt()
}
MyTableModel myModel = new MyTableModel(); // or pass parameters since you're writing the constructor(s)
JTable myTable = new JTable(myModel);
because in the end it's usually easier and more satisfying this way.
[edit: And if you want to call myModel.addData(something) from an ActionListener then you have to go the AbstractTableModel route, since DefaultTableModel doesn't have that functionality. Since MyTableModel is your class, you can write an addData() method for it.]
Btw, your variables CartItems and Cartgui violate standard Java coding conventions. They should start with lower-case letters. However this has no effect whatsoever on whether your code works or not.
I have a JTable and I can't manage to make it update its content, I am in IntelliJ IDEA.
I'll share some code first
I have all the objects declared in the main class, which extends a JFrame
private Object[][] transactionsData;
private String[] transactionsColumnNames = new String[] {"uid","data","ora","product","ini","amount","final"};
private DefaultTableModel transactionsModel;
private JTable transactionsTable;
Then in the createUIComponents() method, that I managed to understand, is called at the very beginning, when the UI needs to be created, I initialize the table like this:
transactionsData = Main.db.getTransactions();
transactionsModel = new DefaultTableModel(transactionsData,transactionsColumnNames);
transactionsTable = new JTable(transactionsData,transactionsColumnNames){
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component component = super.prepareRenderer(renderer, row, column);
int rendererWidth = component.getPreferredSize().width;
TableColumn tableColumn = getColumnModel().getColumn(column);
tableColumn.setPreferredWidth(Math.max(rendererWidth + getIntercellSpacing().width, tableColumn.getPreferredWidth()));
return component;
}
};
transactionsTable.setRowSelectionAllowed(true);
transactionsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
transactionsTable.setDefaultEditor(Object.class, transactionsEditor);
transactionsTable.getTableHeader().setFont(new Font("Segoe",Font.PLAIN,16));
sorter = new TableRowSorter<>(transactionsTable.getModel());
transactionsTable.setRowSorter(sorter);
transactionsTable.getSelectionModel().addListSelectionListener(e -> {
if ( transactionsTable.getSelectedRows().length > 0 ) {
logUIDField.setText(transactionsTable.getValueAt(transactionsTable.getSelectedRow(), 0).toString());
transactionsTable.clearSelection();
uidFilter();
}
});
Now, each time I click a button i switch the content of the frame and the table is displayed, I need it to update its content before being displayed, because its content is taken from an online database, and of course, its content may have changed, so I do this:
storicoButton.addActionListener(e -> {
if( Main.db.hasTag() )
logUIDField.setText(Main.db.tag.getUid());
else
logUIDField.setText("");
transactionsData = Main.db.getTransactions();
transactionsModel = new DefaultTableModel(transactionsData, transactionsColumnNames);
transactionsTable.setModel(transactionsModel);
uidFilter();
CardLayout cardLayout = (CardLayout) rootPanel.getLayout();
cardLayout.show(rootPanel, "Card4");
});
But I can't manage to make it update its content, I tried everything I could find on the internet, and on this site, but everyone says to just use setModel, but it doesn't work, and I can't find what I'm doing wrong.
I tried to read the sizes of transactionsData, transactionsModel and transactionsTable before and after I click the button, transactionsData and transactionsModel actually update, since their size changes accordingly ( I checked looking at the database while I ran the program ) but transactionsTable doesn't change at all.
I tried repaint(), various methods that looks like "updateSomething", I tried with local variables, global variables, initializing new model, updating the model, initializing the table with the model or with data and columns, nothing. I'm desperate.
I hope someone here can help me.
At the moment here is late, and tomorrow I need to wake up early, after work I'll try starting clean, rewriting everything from scratch, maybe I'll find the solution myself, in the mean time I hope someone here could push me in the right direction at least.
Il post the code of that uidFilter() which I found online, it's the first thing I'm going to remove tomorrow, because I suspect it can change something, but I don't have the time to do that now
private void uidFilter() {
RowFilter<TableModel, Object> rf = null;
//If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter(logUIDField.getText(), 0);
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rf);
}
I built a jTable using NetBeans GUI, and I want to update it inside the constructor of the class. I'm planning to add a search option on the frame so the whole update idea is quite critical for me.
My code:
public availableTrumps(TrumpistClient TC){
initComponents();
availableTrumpsTrumpistClient=TC;
String result=null;
String query="SELECT * FROM APP.TRUMPS";
result=this.availableTrumpsTrumpistClient.WritingReading("sql_select", query);
if (result.contains("empty")){
JOptionPane.showMessageDialog(this, "There are now trumps to show.");
}
else if (result.contains("error")){
JOptionPane.showMessageDialog(this, "Error in the connection. Please try again.");
}
else{
int i;
String []data = result.split("\r\n");
String [][] data2 = new String [data.length][];
for (i = 0; i < data.length; i++)
{
data2[i] = data[i].split("&");
}
String[] columnNames = {"From", "To", "Departure Time", "Remaining Places", "Proposer", "ClosingTime", "Cost Per Seat" };
this.jTable1 = new JTable(data2,columnNames);
this.jTable1.setPreferredScrollableViewportSize(new Dimension(500,100));
this.jTable1.setFillsViewportHeight(true);
JScrollPane jps = new JScrollPane(jTable1);
add(jps);
jTable1.revalidate();
}
}
The input two-dimentional array data2 is fine and validated.
I added the last 5 rows of the code to see if they help with something. I don't know if they are mandatory and in any case I do not want to change the graphical properties of the jTable I built with the GUI (just the data in it).
When I run the program, I see that the jTable remains empty.
Why?
I suggest you use a table model, whenever the data changes you change the model. Build the JTable instance only once, not whenever you need to change the data.
As others have said, you don't want to create multiple JTable instances. Create one like this:
DefaultTableModel model = new DefaultTableModel(new Object[0][0],
new String[]{"From", "To", "etc."});
JTable table = new JTable(model);
Then, when you need to add rows, use
model.addRow(dataForThisRow); // Object
If you want to change a cell:
model.setValueAt(newValue, row, col); // Object, int, int
Or, to remove row i:
model.removeRow(i); // int
For more information, see the DefaultTableModel documentation.
If, for some reason, it is imperative that you recreate the table each time, I believe the problem is that you are calling revalidate without calling repaint.
I am facing problem of duplicate rows in the JXTable. If I sort the JXTable data while the new rows are being inserted in JXTable, the final result in JXTable shows duplicate rows that make invalid result in table. Even it also shows correct count of rows that has been inserted but some rows are completely missing whereas some rows are found duplicate in JXTable.
If I sort the JXTable after all data has been inserted successfully then it is showing correct data i.e no duplicate rows and no rows missing.
code example :
I have a method to which I am passing defaultTableModel and here is am adding items in the table
public void addingItems(DefaultTableModel defaultTableModel)
{
for(int i=0;i< numberofItems;i++){
Vector vobject = new Vector();
vobject.add("...");
vobject.add("xxx");
vobject.add("yyy");
...
..
vobject.add("");
defaultTableModel.addRow(vobject);
}
one the other hand I have adding code of sorting at tableHeader actionlistener
tableheader.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
Vector data = defaultTableModel.getDataVector();
Collections.sort(data, new ColumnSorter(colIndex, ascending));
}
});
I have put code in the synchronized block but not getting success.
Please provide a better solution to handle this issue.
I have put code in the synchronized block but not getting success.
Because you have to handle synchronization not only in your sort handler block, but also on each modification operation.
For example:
...
vobject.add("");
synchronized(monitor) {
defaultTableModel.addRow(vobject);
}
}
and
...
Vector data = defaultTableModel.getDataVector();
synchronized(monitor) {
Collections.sort(data, new ColumnSorter(colIndex, ascending));
}
}
In this case your UI could look somewhat unresponsive if you have a lot of data in your table because sort is N^2*logN. If you have somewhat about 200 entries - it wouldn't make a problem for you.
In Java I'm using the DefaultTableModel to dynamically add a column to a JTable.
//create DefaultTableModel with columns and no rows
DefaultTableModel tableModel = new DefaultTableModel(columnNames, 0);
JTable table = new JTable(tableModel);
The columnNames variable is a string array with the column names. So after the program is up and running the user has the option to add additional columns. I do so as follows
tableModel.addColumn("New column name");
Which dynamically adds the column to the table as desired. The user can also remove columns added. For this I use the following code:
TableColumn tcol = table.getColumnModel().getColumn(0);
table.getColumnModel().removeColumn(tcol);
which should remove the column at a specified index, I've also tried:
table.removeColumn(sheet.getColumn(assessmentName));
Both of them work (visually), but here's the problem. After deleting an added column, if another column is added and the table refreshes, the previously deleted column is there again. So while it is removing the column visually, neither of the last two code snippets actually removes it from the model. I'm assuming here that since the column was added to the model that is where it needs to be removed from? Is there a specific method that I need to call or some logic that I need to implement to remove the column?
For your table, try calling table.setAutoCreateColumnsFromModel(false);
This post has a good example as to how to delete column and the underlying data.
I'm assuming here that since the column was added to the model that is where it needs to be removed from?
Yes.
Is there a specific method that I need to call or some logic that I need to implement to remove the column?
No, but you can make up your own method:
moveColumn(...); // to move the column to the end
setColumnCount(...); // to remove the last column
As a side note if you want to give the users the ability to hide/show columns check out the Table Column Manager.
Acting at the TableColumn level, as you show, has only a visual impact but no impact on the TableModel whatsoever.
If you want to really remove a column from DefaultTableModel then you'll need to subclass it and then, in your subclass:
public class MyTableModel extends DefaultTableModel {
public void removeColumn(int column) {
columnIdentifiers.remove(column);
for (Object row: dataVector) {
((Vector) row).remove(column);
}
fireTableStructureChanged();
}
}
I haven't checked it, but it should work in your case.
Of course, removeColumn() should be called only from the EDT.
Note that I wouldn't encourage anyone to produce this kind of code; in particular, using, or deriving from, DefaultTableModel is not the best solution to define a TableModel.
The DefaultDataModel doesn't have a really removeColumn() function, so I wrote a function myself, which can actually solve the problem.
private void removeColumn(int index, JTable myTable){
int nRow= myTable.getRowCount();
int nCol= myTable.getColumnCount()-1;
Object[][] cells= new Object[nRow][nCol];
String[] names= new String[nCol];
for(int j=0; j<nCol; j++){
if(j<index){
names[j]= myTable.getColumnName(j);
for(int i=0; i<nRow; i++){
cells[i][j]= myTable.getValueAt(i, j);
}
}else{
names[j]= myTable.getColumnName(j+1);
for(int i=0; i<nRow; i++){
cells[i][j]= myTable.getValueAt(i, j+1);
}
}
}
DefaultTableModel newModel= new DefaultTableModel(cells, names);
myTable.setModel(newModel);
}