I have a table that shows the results of user defined queries in a swing project. I want to allow the user to extract the data from a particular names column, if present. At the moment I can select the data from a column when I click the column but I don't know how to do the same thing from a button so that only a particular column's data (the column is called HNum) is obtainable. The code I have so far is as follows. If this is impossible I could always try to make sure that HNum is the first column but I need something cleaner I think.
btnCompare.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object[] data_L = columnToArray(table,table.getSelectedColumn());
}
}
public Object[] columnToArray(JTable table, int columnIndex){
// get the row count
int rowCount = table.getModel().getRowCount();
// declare the array
Object [] data = new Object[rowCount];
// fetch the data
for(int i = 0; i < rowCount; i++){
data[i] = table.getModel().getValueAt(i, columnIndex);
}
return(data);
}
Do you tried to use a TableColumnModel ?
You can define all the treatment you need like a getColumnName{}
https://docs.oracle.com/javase/7/docs/api/javax/swing/table/TableColumnModel.html
edit :
an example
http://www.java2s.com/Tutorial/Java/0240__Swing/ExtendingAbstractTableModel.htm
Related
I've investigated lots of different questions and answers around this, but can't find one that seems to work.
I'm new to Java, but have experience in a variety of different languages and, so far (in context to what I'm experimenting with), it's feeling a bit like VBA except with having to build up the actions/functions that you feel should be already there. This is, I expect, just down to my own inexperience though.
I'm using Netbeans IDE 8.2 with Java Runtime 1.80.
I have created jFrame that contains a jTable. The jTable is built with data like so:
public void showTasks(Boolean removeID) {
ArrayList<Tasks> list = tasksList("SELECT * FROM tasks");
JTable table = jTable1;
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
model.addRow(row);
}
// Remove the 'id' column from the table view
if(removeID) { table.removeColumn(table.getColumnModel().getColumn(0)); }
}
The background behind this is less relevant, but essentially tasksList is a function that applies the query to an SQL statement, returning an ArrayList. I build up my rows with 4 columns, then remove the first column so 'ID' is available but not visible (this final action has been segregated through testing/experimentation).
I have another area of code that opens a jDialog when a row is clicked, in which it is possible to update the MySQL DB.
Problem
I'm trying to throw in a function call so that the table data 'refreshes' when the jDialog is closed. I have temporarily added in a button to the jFrame (where the jTable lives) to test/debug this function.
I can't seem to get this to work, though. The closest I have achieved is to re-call showTasks(false), but this obvious just adds rows with updated data, rather than replacing the dataset. I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
As I'm new to Java, and may still be looking at it from a flawed method of thinking, I'm finding it difficult to apply any other examples to that of my own. I also can't seem to find a way to implement fireTableDataChanged().
Surely this is a simple concept I'm over-thinking?
Edit - based on below answer
Is there a reason why something like this would be considered incorrect, if deleting all rows and adding them back in is okay?
public void refreshTasks() {
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
int row_total = model.getRowCount();
for(int i= row_total -1;i>=0;i--) {
model.removeRow(i);
}
showTasks(false);
}
Edit: Button to invoke data update
Now works correctly (if not improperly) with the following:
private DefaultTableModel parentTable; // To store the parent 'Task' table model
public void setStart(int user,DefaultTableModel table) {
this.txt_taskID.setText(Integer.toString(user)); // Converts the ID to a string
addData(user); // Populates the fields
parentTable = table; // Sets parent TableModel to a variable
}
The above code is called from the Parent jFrame when the Dialog is opened, and passes the Table model and the 'ID' of the row I'm looking to edit. The table model is stored in parentTable.
There's also a 'Save' button, and a 'Cancel' button. I'm yet to separate these, and currently 'Save' does just that (SQL update and so on). My 'Cancel' button closes the dialog and refreshes the jTable, as per the below function:
private void btn_CancelActionPerformed(java.awt.event.ActionEvent evt) {
this.setVisible(false); // Hide the dialog
Menu menu = new Menu(); // for accessing the tasksList function
parentTable.setRowCount(0); // Clears the jTable data
// jTable data is then 'rebuilt' using the new data
ArrayList<Tasks> list = menu.tasksList("SELECT * FROM tasks");
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
parentTable.addRow(row);
}
}
I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
Yes that is probably the best practice.
The only other approach is to create a completely new TableModel and add it to the table using the setModel() method. The problem with this approach is that it will reset any custom renderers/editors you may have set on the table.
The easiest way to remove all the rows from the DefaultTableModel is to just use:
model.setRowCount(0);
I'm not sure how you want to do it but I'm gonna give you simple example for deleting and refreshing JTable maybe it's help you.
This following btnDelete Jbutton added to JFrame for deleting rows from table:
btnDelete.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowIndex = table.getSelectedRow();
if(rowIndex > -1) {
int x = (Integer) table.getModel().getValueAt(rowIndex, 0);
if (conn.removeContact(x) == true) { //here add your own code like removeID
model.removeRow(rowIndex);
lblInfo.setText("Contact deleted successfully.");
model.fireTableDataChanged();
} else {
lblInfo.setText("Cannot remove at this time!");
}
}else {
lblInfo.setText("At first you need select a row with single click!");
return;
}
}
});
and these following codes for refreshing table in primitive way :
btnRefresh.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowCount = model.getRowCount();
for (int i = rowCount - 1; i >= 0; i--) {//remove all rows
model.removeRow(i);
}
lblInfo.setText("Table datas updated successfully.");
for (Person p : conn.readAllContacts()) {//add all row from scratch
model.addRow(new Object[] { p.getId(), p.getName(), p.getLastName(), p.getPhone(), p.getEmail() });
}
}
});
Simple question. Is there any way to check if a JTable contains a column before calling getColumn(Object identifier) ?
JTable table = ...;
table.getColumn("header");
getColumn() throws a IllegalArgumentException if the header doesn't exist. So, is catching that exception the only way to check for the column? Looking for a hasColumn() or doesColumnExist() or isColumnValid() but, alas, nothing.
You could simply iterate over the TableColumnModel's available columns and see if it contains the available column, for example...
JTable table = ...;
Object identifier = ...;
TableColumnModel model = table.getColumnModel();
boolean found = false;
for (int index = 0; index < model.getColumnCount(); index++) {
if (model.getColumn(index).getIdentifier().equals(identifier)) {
found = true;
break;
}
}
For example...And yes, I would write this into a helper method/class so you can reuse it.
You should avoid using exceptions as logic controllers, as there might a number of reasons an exception might be raised
Background
I have a JTable called table, and I have a column that is not part of the DefaultTableModel so its invisible:
final JTable table = new JTable(new DefaultTableModel(new Object[]{"Title", "Artist",
"Album", "Time"}, 0)
I add the respective rows like this:
int upTo = songList.size();
int idx = 0;
while (idx < upTo) {
SongObject curSong = songList.get(idx);
model.addRow(new Object[]{
curSong.toString(),
curSong.getArtist(),
"-",
curSong.getDuration(),
curSong});
idx++;
}
Where curSong is the the current song object that it is adding, the SongObject contains all data about the song. The toString() returns the title of the song.
Problem:
The problem is that when I try to access the column like this:
SongObject songToPlay = (SongObject) table.getModel().getValueAt(table.getSelectedRow(), 4);
It throws a java.lang.ArrayIndexOutOfBoundsException: 4 >= 4 exception.
Can anyone explain why and propose a solution?
Thanks in advance :)
DefaultTableModel.addRow() somewhere down the chain executes private justifyRows() method, which trims the unused columns from the row to the size equal to getColumnCount(). So the fifth column is never added to the model. As a result, you get ArrayIndexOutOfBoundsException when you're attempting to access this column.
If you need access to the actual SongObject then you can have a custom model that would return SongObject for a given row index. Make an extension of AbstractTableModel. See How to Use Tables tutorial for examples.
As an alternative, you can still use SongObject in a visible column. Just use a custom renderder that would renderer SongObject as a string for example. See Using Custom Renderers for details. You can reuse DefaultTableModel in this case.
Thanks to Aqua I overrode the following:
final JTable table = new JTable(new DefaultTableModel(new Object[]{"Title", "Artist", "Album", "Time"}, 0) {
#Override
public void addRow(Object[] rowData) {
Vector blah = DefaultTableModel.convertToVector(rowData);
insertRow(super.getRowCount(), blah);
}
#Override
public void insertRow(int row, Vector data) {
super.dataVector.insertElementAt(data, row);
super.fireTableRowsInserted(row, row);
}
});
Then I accessed the item in the fifth column (which is not part of the model!) like this:
SongObject songToPlay = (SongObject) table.getModel().getValueAt(table.convertRowIndexToModel(
table.getSelectedRow()), 4); //get the value at the VIEW location NOT THE MODEL collection
Sorry for the messy code but it worked. Home I could help someone with a similar problem. The solution just misses the justifyRows() method found in DefaultTableModel
ive been trying to update the table after the insertion or deletion of items from a abstract table model but whenever i do that, instead of removing the old records and replace with the new ones, the old rows remains and it creates all the rows again without removing the old ones..so i get duplicate items, this is the code im using :
for the data inserted :
TestModel tm = new TestModel() ;
tm.fireTableRowsInserted(records.length, records.length);
and for the data deleted :
TestModel tm = new TestModel() ;
tm.fireTableRowsDeleted(records.length, records.length);
any clue of how to get around with that?
any help is greatly appreciated!
Kind regards,
Romulo Romero
Create a table with a boolean column. Since using this boolean column you can delete those rows that are selected for deletion. Just like the following screen shot,
Then in your TableModel make a List<StudentDO> such that it will hold all the table data.
Adding a Row:
To add a row just create a new StudentDO and send it to the table model and the model addRow method will add the object to the table list.
Deleting Rows:
For Deleting rows just call a delete method and this should fire event in TableModel such that model should traverse all the rows and check which ever row is selected and delete it.
Note: Deleting rows should be done from the end not from the beginning of the list.
StudentTableModel.java
class StudentTableModel {
// Required methods code goes here.
public void addRow(StudentDO do1) {
data.add(do1);
fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
}
public void deleteRow() {
for(int rowIndex = data.size() - 1; rowIndex >= 0; rowIndex--) {
if(data.get(rowIndex).isSelect()) {
data.remove(rowIndex);
}
}
fireTableDataChanged();
}
}
P.S: fireXXXMethods should be called only in the model. Because any data change will be responsible of the model.
I want to display a database table as a JTable. I have never used JTable before so I googled JTable and TableModel.
With that googling, I am able to write my own custom TableModel which show data stored in
Object[][] data;
Now, I want to show my database table data into JTable. I searched that also and have got an idea of that but still confused about what should goes where in the implementation class of AbstractTableModel.
Following is the code of custom TableModel.
public abstract class AbstractPOLDATTableModel extends AbstractTableModel {
protected boolean DEBUG = false;
private String[] columnNames;
private Object[][] data;
protected AbstractPOLDATTableModel(String[] columnNames, Object[][] data) {
this.columnNames = columnNames;
this.data = data;
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
#Override
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
#Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
if (col < 2) {
return false;
} else {
return true;
}
}
#Override
public void setValueAt(Object value, int row, int col) {
if (DEBUG) {
System.out.println("Setting value at " + row + "," + col
+ " to " + value
+ " (an instance of "
+ value.getClass() + ")");
}
data[row][col] = value;
fireTableCellUpdated(row, col);
if (DEBUG) {
System.out.println("New value of data:");
printDebugData();
}
}
private void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
Now, how to change the above code so that my JTable can have the follwing features:
It shows data from the database
User can edit the table directly and when he clicks on a "save" button, the changes reflect in the database data
User can insert data directly.
User can delete data directly.
I'm assuming you've implemented 1 according to what Adamski suggested in your previous question. Also, as suggested by Sanoj, change to using some sort of List to store your data.
To support 2-4, you must ensure that the data you're pulling out of the database comes from only one table and involves no derived data (eg. aggregations, column1 + column2). You will need to keep track of your where clause, if you intend to let the user filter rows.
Within each Row, store another Row (let's call it updatedRow) that represents the updates the user has made using the GUI. Once any update is made to the row, this field needs to be populated with a new Row containing the updated data . When "Save" is clicked, you run update queries for all Rows with a non-null updatedRow, updating the database with data in updatedRow which do not match those from the original Row (don't update if the user changes data back to how it was originally). You might also have another "Undo Updates" button which populates the table with the original data for all Rows with a non-null updatedRow.
I would strongly recommend that you store additional metadata on which fields form the primary key and prevent those from being changed, as updates to such columns may be expensive (since they are presumably indexed and may have some RI constraints attached). This should be reflected in the GUI by making such columns not editable. The where clause in updates would use only these fields rather than all fields (you will still need other filters that your user set in 1).
I'd suggest a two step process. The user starts by clicking a button which adds a row to the table. After entering data, the user clicks another button to insert into the database (or you can combine this functionality with updates in the "save" button). You will need to allow primary key fields to be editable for newly inserted rows if the primary key columns aren't some auto-generated ID.
To different between which Rows are already in the database and which ones aren't, I'd suggest storing a list of newly inserted Rows.
If you let the user pick only certain columns to display in the table, you will need to determine how to deal with the columns that aren't being displayed (eg. let the database set a default, populate with auto-generated ID).
Probably best to implement this by having a checkbox with each Row, then when the "Delete" button is clicked, it calls SQL to delete each checked Row using the filter from 1 and primary key metadata mentioned in 2, as well as to remove it from the table.
General considerations:
The same setValueAt method will be used for both updated and inserted Rows, but you want different behaviour. You want to set updatedRow for updates, but not when editting Rows you're about to insert.
How will you handle errors such as constraints not being met or invalid input (eg. 'abcde' in a numeric field)?
SQL injection.