I've working on a GUI that the user can browse text files from the SYSTEM and then when the user press "Start" button the program reading the text file/s, create lists from its data and supposed to add it to TableView. I'm stuck on inserting the data from the lists to the table. I've created the columns names by file names and added it to table:
tblConfigurationSystemColumns.add("Parameter Name");
tblSystemColumn.stream().map((str) -> str.split("PCM")).forEachOrdered((a) -> {
tblConfigurationSystemColumns.add(a[0].trim());
});
for (int i = 0; i < tblConfigurationSystemColumns.size(); i++) {
TableColumn col = new TableColumn(tblConfigurationSystemColumns.get(i));
tableConfigurationSystem.getColumns().addAll(col);
}
The column names coming from the list tblConfigurationSystemColumns. This list may be changed from each use of the GUI by number of file you browse from the system. (for now let think that we have 2 strings inside: "column1","column2")
I need to add items to column1 from the list SysParameter , and to column2 from list SysValues.
How can I add values from each list to each column by rows?
If you need any more code please tell me (just let you know, the only code that I have it the list creating from the files).
EDIT:
This is what I got after the column building.
after this I need to get the "Parameter" and the "Value" for each column(as you can see).
I've made a list that get the "Parameter" from the text file, and another list that get the "Value" from the text file.
how can I put each list to it's column?
This is the code that build this lists:
boolean inCESystem = false;
for (final String line : list) {
if (line.contains("CE-") && !(line.contains("CE-system-equipment-pm") || line.contains("inbound") || line.contains("outbound"))) {
inCESystem = true;
}
else if (line.trim().isEmpty()) {
inCESystem = false;
}
else if (inCESystem) {
CE_System.add(line);
}
}
boolean inCESystemInbound = false;
for (final String line : list) {
if (line.contains("CE-") && (line.contains("inbound")) ) {
inCESystemInbound = true;
}
else if (line.trim().isEmpty()) {
inCESystemInbound = false;
}
else if (inCESystemInbound) {
CE_System.add("inbound_loadlock - "+line.trim());
}
}
boolean inCESystemOutbound = false;
for (final String line : list) {
if (line.contains("CE-") && (line.contains("outbound")) ) {
inCESystemOutbound = true;
}
else if (line.trim().isEmpty()) {
inCESystemOutbound = false;
}
else if (inCESystemOutbound) {
CE_System.add("outbound_loadlock - "+line.trim());
}
}
/*
* Check the CE list to split each object per parameter and value to different lists
*/
CE_System.stream().map((str) -> str.split(",")).map((a) -> {
CE_SystemParameter.add(a[0].trim()); //Parameters
return a;
}).forEachOrdered((a) -> {
if(a.length > 1) {
CE_System_Value.add(a[1].trim()); //Values
} else {
CE_System_Value.add(""); //add blank if parameter doesn't have value
}
});
EDIT 2: Text file example
CE-system:
No features to set for this item...
CE-system-componentmanager:
Bootstrap Parallelism ,Parallel Bootstrapping
CE-system-components:
No features to set for this item...
CE-system-components-accessmanager:
Access control enable ,disabled
Access policy prototyping ,enabled
Access user group ,enabled
Implicit roles access policy ,disabled
World access policy ,disabled
CE-system-components-eqlog:
EquipmentLog Enable ,false
Line that contains "CE-" its just title to know that is should be in the "Configuration" Tab.
each line inside is the "parameter" and the value(after the comma).
EDIT 3: The table should look like this example (This example is from my code in Java SWT)
Thank you very much guys.
The data for a TableView is held in the ObservableList of the items property. A TableView is designed to hold a list of POJOs that contain various properties. Each of the properties will correspond to a TableColumn who obtains the value of these properties using a Callback.
Since you are browsing text files let's say you define a POJO like so:
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.SimpleStringProperty;
public class TextFile {
private final StringProperty name = new SimpleStringProperty(this, "name");
public final void setName(String name) { this.name.set(name); }
public final String getName() { return name.get(); }
public final StringProperty nameProperty() { return name; }
private final LongProperty size = new SimpleLongProperty(this, "size");
public final void setSize(long size) { this.size.set(size); }
public final long getSize() { return size.get(); }
public final LongProperty sizeProperty() { return size; }
public TextFile() {}
public TextFile(String name, long size) {
setName(name);
setSize(size);
}
}
From this you'll want a TableView of TextFiles that has a TableColumn for name and a TableColumn for size. To tell a TableColumn how to obtain the correct value you set the cellValueFactory with the appropriate Callback. This Callback accepts a TableColumn.CellDataFeatures and returns an ObservableValue. If the ObservableValue changes the TableColumn will update the item of the corresponding TableCell.
ObservableList<TextFile> files = ...;
TableView<TextFile> table = new TableView<>();
table.setItems(files);
TableColumn<TextFile, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(features -> features.getValue().nameProperty());
table.getColumns().add(nameCol);
TableColumn<TextFile, Number> sizeCol = new TableColumn<>("Size");
sizeCol.setCellValueFactory(features -> features.getValue().sizeProperty());
table.getColumns().add(sizeCol);
Note that each TextFile in files is a row in the TableView.
I guess you are looking for something like that:
TableColumn< YourObject, String> col = new TableColumn<>();
col.setCellValueFactory(new PropertyValueFactory("nameOfThePropertyYouWantToDisplay");
TableColumn< YourObject, String> col2 ....
TableView < YourObject> table = new TableView();
table.setItems(observableListOfYourObject);
Look here for a detailed description: https://docs.oracle.com/javafx/2/ui_controls/table-view.htm
Choose the item type accordingly. Your description indicates the following properties:
The table data is not edited once it's loaded.
You cannot hardcode the number of files.
Therefore a suitable choice of data structure would be List<String>. Each list contains one element for every column.
public void initializeTableColumns(TableView<List<String>> table, File file, File... files) {
List<String> fileItems = readFile(file);
TableColumn<List<String>, String> column = new TableColumn<>(file.getName());
column.setCellValueFactory(cd -> new SimpleStringProperty(cd.getValue().get(0));
table.getColumns().add(column);
for (String s : fileItems) {
List<String> row = new ArrayList<>(files.length + 1);
row.add(s);
table.getItems().add(row);
}
for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
File f = files[fileIndex];
fileItems = readFile(f);
int itemCount = Math.min(fileItems.size(), table.getItems().size());
// add items from file
for (int i = 0; i < itemCount; i++) {
table.getItems().get(i).add(fileItems.get(i));
}
if (itemCount <= table.getItems.size()) {
// fill items that may be missing
for (int i = itemCount; i < table.getItems().size(); i++) {
table.getItems().get(i).add(null);
}
} else {
// add missing rows
for (int i = table.getItems.size(); i < itemCount; i++) {
List<String> row = new ArrayList<>(files.length + 1);
for (int j = 0; j <= fileIndex; j++) {
row.add(null);
}
row.add(fileItems.get(i));
table.getItems().add(row);
}
}
final index = fileIndex + 1;
column = new TableColumn<>(f.getName());
column.setTableColumn(cd -> new SimpleStringProperty(cd.getValue().get(index)));
table.getColumns().add(column);
}
}
Related
I got a TableView<MonthRow> where I want to display each month of the year and its relevant days.
I got those three classes for presentation purposes:
public record DayCell(
LocalDate date,
DayType type
) {
public String nameOfDay() {
return DateTimeFormatter.ofPattern("EE", Locale.ENGLISH).format(date);
}
}
public enum DayType {
COMPASSIONATE_LEAVE("C"),
EMPTY("E"),
NOT_ON_PAYROLL("N"),
QUARANTINE("Q"),
REGULAR_LEAVE("R"),
SICK_LEAVE("S"),
TRAINING("TRG"),
TRAVEL("T"),
UNPAID_LEAVE("U");
private final String shortName;
DayType(String shortName) {
this.shortName = shortName;
}
}
public record MonthRow(
String name,
DayCell[] days // amount of days in that specific month
) {
}
And then I create the table content:
public ObservableList<MonthRow> createMonth(int year) {
MonthRow[] months = new MonthRow[12];
for (int i = 0; i < months.length; i++) {
LocalDate date = LocalDate.of(year, i + 1, 1);
months[i] = new MonthRow(date.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH), createCells(date));
}
return FXCollections.observableArrayList(months);
}
public DayCell[] createCells(LocalDate date) {
DayCell[] cells = new DayCell[date.lengthOfMonth()];
for (int i = 0; i < cells.length; i++) {
cells[i] = new DayCell(date.plusDays(i), DayType.EMPTY);
}
return cells;
}
Having the ObservableList now for the TableView, I am kinda stuck. I want to have one TableColumn for the month and 31 TableColumns for the DayCells. However, because it's an array I am not sure how to reflect the cellData of each DayCell into each TableCell.
Also, because some months do not have 31 days and therefore, the
cells should not show any content for the missing days.
Each cell should show the nameOfDay() content and colouring according to the DayType (will be done in the respective cellFactory at some point).
TableView Example
This might be a completely wrong approach, so please don't hesitate to guide me towards a different solution.
You can do something like this:
TableView<MonthRow> table = new TableView<>();
TableColumn<MonthRow, String> monthColumn = new TableColumn<>("Month");
monthColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName()));
table.getColumns().add(monthColumn);
for (int i = 0; i < 31 ; i++) {
TableColumn<MonthRow, DayCell> dayColumn = new TableColumn<>(Integer.toString(i+1));
final int index = i ;
dayColumn.setCellValueFactory(cellData -> {
MonthRow month = cellData.getValue();
if (index < month.getDays().size()) {
return new SimpleObjectProperty<>(month.getDays()[index]);
} else {
return new SimpleObjectProperty<>(null);
}
});
dayColumn.setCellFactory(new TableCell<>() {
#Override
protected void updateItem(DayCell day, boolean empty) {
super.updateItem(day, empty);
if (empty | day == null) {
setText("");
} else {
setText(day.nameOfDay());
}
}
});
table.getColumns().add(dayColumn);
}
This might be a completely wrong approach
Maybe. Do you need the functionality of a TableView here? E.g. selection of a month (row), etc? Perhaps a simple GridPane inside a ScrollPane would be a more convenient approach; it really depends on your project requirements.
I use:
Java 10 SE
Java Swing
Eclipse IDE
I have JTable, the contents gets loaded at runtime dynamically. It has some JComboBoxes. If I select the JComboBox, and then attempt to reload the table, the JComboBox appears visible at the time when the table loading is in progress.
Besides that, if the JComboBox's contents gets updated (elsewhere in different table, when the combo supposed to reflect that new contents), that new contents does not get visible staright away after loading the JTable dynamically.
The snap-shot sample of the app:
That's, the table being loaded at runtime up, and in the middle you have vsisble JComboBox persistent from the previous selection.
How to:
Get rid off that persistent JComboBox
Make the data visible instantly, upon update under the combo, once you load the table dynamically
I have the public final class TableColumnEditor extends DefaultCellEditor{
which returns the JComboBox on a specific column:
else if(row == ROW_2_DEVS_WORK_WEEKEND) {
ProjectMetrics metrics = new ProjectMetrics();
JComboBox<String> combo = new JComboBox<>();
combo.setBackground(Color.WHITE);
for(String devs : metrics.identifyDevsThatWorkAtWeekend()) {
combo.addItem(devs);
}
return combo;
}
I have the public final class TableColumnRenderer extends DefaultTableCellRenderer{
which makes sure that the view displays the JComboBox under that specific column:
else if(row == ROW_2_DEVS_WORK_WEEKEND) {
ProjectMetrics metrics = new ProjectMetrics();
JComboBox<String> combo = new JComboBox<>();
combo.setBackground(Color.WHITE);
for(String devs : metrics.identifyDevsThatWorkAtWeekend()) {
combo.addItem(devs);
break;
}
return combo;
}
The table gets loaded dynamically right here (non-essential things removed):
public static void reloadTableDynamically(JTable metricsTable){
DefaultTableModel model = (DefaultTableModel)metricsTable.getModel();
if(projectData.isEmpty()) {
metricsTable.clearSelection();
int rowCount = model.getRowCount();
for(int item = (rowCount - 1); item >= 0; item--) {
model.removeRow(item);//clears previous rows
}
metricsTable.repaint();
return;
}
model.getDataVector().clear();
int rowCount = constantRows + ((devsTask.size() == 0) ? 1 : devsTask.size());
try {
new Thread(()-> {
int lastRowID = 0;
int devsTaskID = 0;
for(int item = 0; item < rowCount; item++) {
Object[] input = null;
if(item == 0) {
input = new Object[] {"", metrics.getProjectDateRange(), "" };
}//similar branches removed
else {
devsTaskID++;
input = new Object[] {"", devsTask.get(devsTaskID).getDeveloper(), ""};
}
model.addRow(input);
metricsTable.scrollRectToVisible(new java.awt.Rectangle(metricsTable.getCellRect(lastRowID++, 0, true)));
metricsTable.repaint();
try {
Thread.sleep(Config.getInstance().getReloadInOutTable());
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
metricsTable.scrollRectToVisible(new java.awt.Rectangle(metricsTable.getCellRect(projectData.size() - 1, 0, true)));
metricsTable.repaint();//so that to reach the last row
}).start();
}
catch(Exception e) {
}
}
What do you think?
Well, I figured out how to overcome this problem.
Firstly, the JComboBox gets updated on EDT(Event Despatch Thread).
/**
* #param combo The JComboBox ref.
* #param toDisplay The value to add to it
*/
public static void updateComboBoxOnEventDespatchThread(JComboBox<String> combo, String toDisplay) {
Runnable doComboUpdate = new Runnable() {
public void run() {
combo.addItem(toDisplay);
}
};
SwingUtilities.invokeLater(doComboUpdate);
}
Under the JTable column editor:
else if(row == ROW_2_DEVS_WORK_WEEKEND) {
ProjectMetrics metrics = new ProjectMetrics();
JComboBox<String> combo = new JComboBox<>();
combo.setBackground(Color.WHITE);
Runnable doComboInsert = new Runnable() {
public void run() {
int id = 0;
for(String devs : metrics.identifyDevsThatWorkAtWeekend()) {
UIutils.updateComboBoxOnEventDespatchThread(combo, "("+ ++id +") " + devs);
}
}
};
SwingUtilities.invokeLater(doComboInsert);
return combo;
}
But the main fix, without which both issues do not go away, is following.
That is, I noticed that in order for data to appear under the table instantly, firstly, you need to select any other unrelated table's cell.
That is, the Java thread, which loads the JTable at runtime, does need to have this:
if(model.getRowCount() > 0) {
metricsTable.selectAll();
}
That's probably a hack, but it works for me!
I have a JTable which I create dynamically from List<String> objects. I do this probably completely wrong but it works. The only thing I can't get to work is adding Images to some of the cells.
All it does is, it adds the ImageIcon Object name as String to the cells. See my code below.
private static Image doneImage = getIconImage("doneImage");
private static Image notDoneImage = getIconImage("notDoneImage");
private DefaultTableModel model = new DefaultTableModel(){
#Override
public Class<?> getColumnClass(int column){
if ((column & 1) != 0 ){
return ImageIcon.class;
}else{
return String.class;
}
}
};
initTables();
JTable table = new JTable();
table.setModel(model);
private void initTables(){
model.addRow(new Object[]{});
int rowsToAdd = 0;
int rowCount = 0;
int columnId = 0;
for(HouseObject aHouse : houses){
for(RoomObject aRoom : aHouse.getRooms()){
model.addColumn(null);
model.addColumn(aRoom.getId());
model.setValueAt(aRoom.getId(), 0, columnId);
if (rowCount < aRoom.getEvents().size()){
rowsToAdd = aRoom.getEvents().size() - model.getRowCount();
for(int i = 0; i <= rowsToAdd; i++){
model.addRow(new Object[]{});
}
rowCount = model.getRowCount();
}
for(int i = 0; i < aRoom.getEvents().size(); i++){
model.setValueAt(aRoom.getEvents().get(i).getId(), i+1, columnId);
for(String houseDone : housesDone){
if(aRoom.getEvents().get(i).getId().contains(houseDone)){
model.setValueAt(doneImage , i+1, columnId+1); // this does not work
}else{
model.setValueAt(notDoneImage, i+1, columnId+1);
}
}
}
columnId = columnId+2;
}
}
}
You need to install renderer for your table
Here is the renderer:
public class IconTableCellRenderer extends DefaultTableCellRenderer {
#Override
protected void setValue(Object value) {
if (value instanceof Icon) {
setText(null);
setIcon((Icon) value);
} else {
super.setValue(value);
}
}
}
And so you must install it:
JTable table = new JTable();
table.setModel(model);
table.setDefaultRenderer(ImageIcon.class, new IconTableCellRenderer());
I have a JTable which I create dynamically from List objects.
Well you can't just add Strings to the table since then image will need to be added as an ImageIcon. So you would need a List so you can add String and Icon values.
Then you need to override the getColumnClass(...) method of your TableModel to return Icon.class for the column that contains the Icon. The table will then use the appropriate renderer for the Icon.
See: How to set icon in a column of JTable? for a working example.
I have stumbled upon a problem when reading in an Excel document, specifically acquiring drop down values (Data Validation) from a Cells. I am able to get the values defined explicitly.
I am able to get the values (720x486, etc) with the following by seeing if cell is within CellRangeAddress.:
Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();
List<? extends DataValidation> dataValidations = sheet.getDataValidations();
for(DataValidation dataValidation : dataValidations)
{
for(CellRangeAddress cellRangeAddress : dataValidation.getRegions().getCellRangeAddresses())
{
String[] explicitListValues = dataValidation.getValidationConstraint().getExplicitListValues();
if(explicitListValues == null)
{
continue;
}
dropDownValues.put(cellRangeAddress, explicitListValues);
}
}
The code above works only for explicit values. The problem I see is when a range is defined in the source of the Data Validation for a cell:
sheet.getDataValidations();
Does not return anything in regards to the range or any info on the Data Validations. Has anyone been able to get a hold of the Source and evaluate the formula to attain values?
I was able to retrieve the data validations defined by a formula for Excel Sheets newer than 2003.
I had to parse the XSSFSheet for the specific info and then reconstruct and evaluate formula.
Here is what I did to attain all DataValidation values:
Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();
List<ExtendedDataValidations> extendedDataValidationsList = getExtendedDataValidations(sheet);
for (ExtendedDataValidations extendedDataValidations : extendedDataValidationsList)
{
AreaReference formulaReference = new AreaReference(extendedDataValidations.formula);
CellReference[] allReferencedCells = formulaReference.getAllReferencedCells();
FormulaEvaluator formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
String[] values = new String[allReferencedCells.length];
for (int j = 0; j < allReferencedCells.length; j++)
{
CellReference cellReference = allReferencedCells[j];
Sheet valueSheet = wb.getSheet(cellReference.getSheetName());
Cell cell = valueSheet.getRow(cellReference.getRow()).getCell(cellReference.getCol());
CellValue evaluate = formulaEvaluator.evaluate(cell);
values[j] = StringUtils.trimToEmpty(StringUtils.removeStart(StringUtils.removeEnd(evaluate.formatAsString(), "\""), "\""));
}
String stRef = extendedDataValidations.sqref;
String[] regions = stRef.split(" ");
for (String region : regions)
{
String[] parts = region.split(":");
CellReference begin = new CellReference(parts[0]);
CellReference end = parts.length > 1 ? new CellReference(parts[1]) : begin;
CellRangeAddress cellRangeAddress = new CellRangeAddress(begin.getRow(), end.getRow(), begin.getCol(), end.getCol());
dropDownValues.put(cellRangeAddress, values);
}
}
In addition I defined a Struc for the formula and cell reference.
private static class ExtendedDataValidations
{
public String formula;
public String sqref;
}
getExtendedDataValidations grabbed the CTExtensionList where the data validation forumla appeared in the sheet:
public static List<ExtendedDataValidations> getExtendedDataValidations(Sheet sheet)
{
List<ExtendedDataValidations> extendedDataValidationsList = new ArrayList<>();
if (sheet instanceof XSSFSheet)
{
CTExtensionList extLst = ((XSSFSheet) sheet).getCTWorksheet().getExtLst();
if (extLst == null)
{
return extendedDataValidationsList;
}
CTExtension[] extArray = extLst.getExtArray();
List<Node> dataValidationNodes = new ArrayList<>();
for (CTExtension anExtArray : extArray)
{
searchForDataValidation(anExtArray.getDomNode(), dataValidationNodes);
}
for (Node dataValidationNode : dataValidationNodes)
{
ExtendedDataValidations dataValidations = new ExtendedDataValidations();
getDataValidationInfo(dataValidationNode, dataValidations);
extendedDataValidationsList.add(dataValidations);
}
}
return extendedDataValidationsList;
}
searchForDataValidation had to traverse the DOM nodes of the sheet looking for specific info on DataValidation. If found Save it in List:
private static void searchForDataValidation(Node node, List<Node> nodesInQuestion)
{
if (StringUtils.equalsIgnoreCase("x14:dataValidation", node.getNodeName()))
{
nodesInQuestion.add(node);
return;
}
for (int i = 0; i < node.getChildNodes().getLength(); i++)
{
searchForDataValidation(node.getChildNodes().item(i), nodesInQuestion);
}
}
getDataValidationInfo was in charge of getting the formula and Cell Reference.
private static void getDataValidationInfo(Node node, ExtendedDataValidations dataValidations)
{
if (StringUtils.equalsIgnoreCase("#text", node.getNodeName()))
{
if (StringUtils.equalsIgnoreCase("xm:sqref", node.getParentNode().getNodeName()))
{
dataValidations.sqref = node.getNodeValue();
}
else if (StringUtils.equalsIgnoreCase("xm:f", node.getParentNode().getNodeName()))
{
dataValidations.formula = node.getNodeValue();
}
return;
}
for (int i = 0; i < node.getChildNodes().getLength(); i++)
{
getDataValidationInfo(node.getChildNodes().item(i), dataValidations);
}
}
Might appear to be complicated, but it does the trick. Hope it helps!
I have two Jlists of vectors filled with data extracted from a mysql db. What I want is when the user select an item (menu) from Jlist1 (which I called menuList) Jlist2 (which I called productList) must display the products of that menu and other things, such as the ability to insert a new product in THAT menu or in a new menu just created.
I've accomplished this task in a way which I think is weak, by using some boolean variables which tells if the user is inserting a product in an existing menu or in a newly created one. Please, can you suggest me a better solution (if exists)? Here is an extract of the most significant part of the code, this is the method which saves a new product in the db:
private void bAddProdActionPerformed(java.awt.event.ActionEvent evt) {
//If new menu is saved, get the new menu's Id
if (newMenuIsSaved == true) {
Product newProduct = new Product();
newMenuId = DBConnection.getNewMenuId();
newProduct.setMenuId(newMenuId);
newProduct.setProductName(productName.getText());
if (checkPPriceValidity(productPrice.getText(), newProduct)) {
int result = DBConnection.insertProduct(newProduct);
if (result == 1) {
reloadProductList();
disableProductButtons();
}
}
} else {
Product newProduct = new Product();
//If a new menu wasn't saved, get the menuId from the selected one (from menuList):
Menu selectedMenu = (Menu) menuList.getSelectedValue();
newProduct.setMenuId(selectedMenu.getMenuId());
newProduct.setProductName(productName.getText());
if (checkPPriceValidity(productPrice.getText(), newProduct)) {
int result = DBConnection.insertProduct(newProduct);
if (result == 1) {
reloadProductList();
newMenuIsSaved = false;
disableProductButtons();
bNewProduct.setEnabled(true);
}
}
}
}
And here is the method reloadProductList():
private void reloadProductList() {
modelProductList.clear();
if (newMenuIsSaved) {
Vector<Product> productVoices = DBConnection.fillProductList(newMenuId);
for (int i = 0; i < productVoices.size(); i++) {
modelProductList.addElement((Product) productVoices.get(i));
}
} else {
Vector<Product> productVoices = DBConnection.fillProductList(selectedMenuId);
for (int i = 0; i < productVoices.size(); i++) {
modelProductList.addElement((Product) productVoices.get(i));
}
}
}
Thank you very much.
One way is to add a ListSelectionListener to menuList and have the handler set the other list's model to display the details for the selected row.