JavaFX Float Column with two decimals - sorting

I want to print floats in a TableView with to decimals. But formatting the floats destroyes the sorting of the column.
TableColumn<Model, String> profit = new TableColumn<Model, String>("Profit");
profit.setCellValueFactory(new PropertyValueFactory<Model, String>("profit"));
profit.setCellValueFactory(cellData -> Bindings.format("%.2f", cellData.getValue().getProfit()));
If i dont formatting the column the sorting is correct. But the Table shows not two decimals every time.
TableColumn<Model, Float> profit = new TableColumn<Model, Float>("Profit");
profit.setCellValueFactory(new PropertyValueFactory<Model, Float>("profit"));

Use a cell value factory to determine what data the cells display, and use a cell factory to determine how the cells should display those data:
TableColumn<Model, Float> profit = new TableColumn<Model, Float>("Profit");
profit.setCellValueFactory(new PropertyValueFactory<Model, Float>("profit"));
profit.setCellFactory(tc -> new TableCell<Model, Float>() {
#Override
protected void updateItem(Float profit, boolean empty) {
super.updateItem(profit, empty);
if (empty) {
setText(null);
} else {
setText(String.format("%.2f", profit.floatValue()));
}
}
});

Related

SWT: Hyperlink in TableViewerColumn with MouseListener and correct column width

I want to add a Hyperlink (or a text that is styled like a Hyperlink) in a TableViewerColumn Cell.
I tried this and this.
Summarize: I either get a clickable Hyperlink for which the text is cropped (the column width is too small) or I get a link that is not clickable (MouseListener missing, no cursor).
Both tries are making use of a StyledCellLabelProvider for the TableViewerColumn. The first try does this:
TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
column .getColumn().setText(TITLE);
column .setLabelProvider(new MyHyperlinkLabelProvider());
However, the text in the cell is cropped and I have no idea how to set the column width so that the text fits in the cell. I tried with using pack(), but it had no effect.
private final class MyHyperlinkLabelProvider extends StyledCellLabelProvider {
private MyHyperlinkLabelProvider() {
}
#Override
public void update(ViewerCell cell) {
TableItem item = (TableItem)cell.getItem();
String myText= "Hyperlink text, unfortunately cropped";
link = new MyHyperlink((Composite)cell.getViewerRow().getControl(), SWT.NONE);
toolkit.adapt(link);
link.setText(myText);
TableEditor editor = new TableEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
editor.setEditor(link, item, cell.getColumnIndex());
GridDataFactory.fillDefaults().applyTo(editor.getEditor());
editor.layout();
link.addMouseListener(new MouseAdapter() {
#Override
public void mouseUp(MouseEvent event) {
super.mouseUp(event);
if (event.getSource() instanceof MyHyperlink) {
MyHyperlink link = (MyHyperlink)event.getSource();
System.out.println("Label was clicked: " + link.getText());
}
}
});
super.update(cell);
}
}
private class MyHyperlink extends Hyperlink {
public MyHyperlink(Composite parent, int style) {
super(parent, style);
this.setUnderlined(true);
}
}
The less preferred alternative is given in the second link. If I cannot get the column width correct, I'd go with this.
The second link suggests to try it with StyledString instead of Hyperlink. The StyledString at least is shown in full width and the column has the correct width. However, you cannot add a MouseListener to a StyledString. Only on the table, but that doesn't help.
private final class MyHyperlinkLabelProvider extends StyledCellLabelProvider {
private MyHyperlinkLabelProvider() {
}
#Override
public void update(ViewerCell cell) {
TableItem item = (TableItem)cell.getItem();
String mytext= "This is the hyperlink text";
/* make text look like a link */
StyledString text = new StyledString();
StyleRange myStyledRange =
new StyleRange(0, phase.length(), Display.getCurrent().getSystemColor(SWT.COLOR_BLUE), null);
myStyledRange.underline = true;
text.append(mytext, StyledString.DECORATIONS_STYLER);
cell.setText(text.toString());
StyleRange[] range = {myStyledRange };
cell.setStyleRanges(range);
super.update(cell);
}
}
To have the column size computed correctly, the cell text has to be set. Although the Hyperlink is what should be displayed link.setText(myText); is not enough. cell.setText(myText) is needed, too.
One can see it in the second snippet, for which the column size is correct. It is because of the cell.setText() call.

Mouse Click Events on JTable

I want to get the index of selected row when user double clicks on a row.
Here is my code:
tab.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int selectedRow = tab.getSelectedRow();
try {
String file = rows[selectedRow][2];
String path = "C:\\Users\\raj kumar\\Gallery\\" + file;
JLabel fileLable = new JLabel();
fileLable.setBounds(500, 600, 300, 300);
fileLable.setIcon(new ImageIcon(path));
pan.add(fileLable);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
});
But the tab.getSelectedRow() returns -1 even though I double clicked the row in the table.
You want to know on which row your mouse points, but you ask which row is selected. So the simple solution is, instead of
int selectedRow = tab.getSelectedRow();
you can use
int row = tab.rowAtPoint(e.getPoint());
to get the wanted row. The Event e has every necessary information you need. The e.getPoint() returns the exact Point your cursor is currently located. And the rowAtPoint() should be self explaining.
This also makes sure that you only work with one row at a time, if this is important to you. I don't know how getSelectedRow() works if multiple rows are selected.

Populating Data in TableView by scrolling

I'm trying to familiarise myself with Java-FX.
I have an ObservableArrayList with approx. 100000 values and I want to populate them in a TableView.
I noticed that populating all data in a TableView performs very slow. Therefore, I decided to improve the performance by
deviding the data set into 10 blocks with 10000 values. Each block should be populated when the scrollBar of the table
reaches its end position. I want to realize it a listiner similar to swing.
Is there an out-of-the-box functionality in Java8 for my problem or any ideas of how to do it?
Here is a code snippet of how I load the file and put the data in the TableView
#FXML private void loadFile() {
IOFileOperations io = new IOFileOperations();
data = io.getData();
if (data==null) {
table.setItems(null);
}
else {
int dataSizeRow=data.size();
int colNumSize = io.getNumberOfColumns();
DataManager.setColNums(colNumSize);
DataManager.setDatas(data);
for(int i=0;i<colNumSize;i++) {
final int x=i;
String tmp="["+Integer.toString(i+1)+"]";
column = new TableColumn<>(tmp);
column.setCellValueFactory(cellData -> cellData.getValue().dataProperty(x));
table.getColumns().add(column);
}
table.setItems(data);
}
}

javafx, TableView: detect a doubleclick on a cell

Given a TableView, i need to detect the doubleclick on a cell.
tableView.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
if(event.getClickCount()>1)
{
System.out.println("double clicked!");
}
}
});
How to determine the cell on which the mouse has been clicked?
Code example.
Run the "Example 12-11: Alternative Solution Of Cell Editing" of official tableview tutorial.
Replace the followings:
table.setEditable(false);
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
TableCell cell = new TableCell<Person, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Cell text: " + c.getText());
}
}
});
return cell;
}
};
No need to EditingCell since your cells are uneditable. Cell factory is used for cell rendering. So one can put any node/control other than default Labeled using cell's setGraphics() method. IMO you cannot access the default cell directly so you should define your own cell factory to be able to put event filter on cell.
JavaFX allows you to set up multiple listeners per cell (I'm not saying that this is good or bad, just that you can). Each listener will execute your code if you have code set to execute a response to the specific listener for the specific column/row. To capture cell mouse clicks, I use the following:
table.setEditable(true);
table.getSelectionModel().setCellSelectionEnabled(true); // selects cell only, not the whole row
table.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
#SuppressWarnings("rawtypes")
TablePosition pos = table.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
int col = pos.getColumn();
#SuppressWarnings("rawtypes")
TableColumn column = pos.getTableColumn();
String val = column.getCellData(row).toString(); System.out.println("Selected Value, " + val + ", Column: " + col + ", Row: " + row);
if ( col == 2 ) { ... do something ... }
if ( col == 5 ) { ... do something ... }
if ( col == 6 ) { ... do something ... }
if ( col == 8 ) { ... do something ... }
}
}
});
You can see from the above code, on the columns I want to do something based on a mouse click, I have code:
if ( col == <int> ) { ... do something ... }
I also have those columns set to not allow editing:
thisCol.setEditable(false);
The rows that I want to edit I have .setEditable(true) but don't have a response included with a mouse click.
Cell editing defaults to 2 mouse clicks. You can change the above code to capture different mouse events on a cell, so you can still edit the cell with 2 mouse clicks, or open a URL, dialog box, etc., with any other mouse event determined by you. TableView allows you to determine your own functionality based on your imagination and programming skills. You're not stuck with "I can either edit it, or fire a mouse event with it." You can do both :)
Add the following in the body of your listener, with T the type of your table record :
#SuppressWarnings("rawtypes")
ObservableList<TablePosition> cells = tableView.getSelectionModel().getSelectedCells();
for( TablePosition< T, ? > cell : cells )
{
System.out.println( cell.getColumn());
}// for
Create your cell using a cell factory and in the cell factory which creates the cell node, place an mouse event handler or filter on the node rather than the tableView.
In my case i use next code
tableViewObject.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (t.getClickCount() == 2 && getSelectedItem() != null) {
SMPBLogger.logInfo("Double cliked", Boolean.TRUE);
if (listener != null) {
listener.doubleClicked(tableViewObject.this,getSelectedItem());
}
}
}
});

JfreeChart limit the number of legend items displayed

I'm in charge of maintaining an application which can draw graphs using JFreeChart.
The application is written in Eclipse-RCP and SWT and use a ChartComposite to display the charts.
The ChartComposite has been partially overridden in order to customize contextual menus depending on the selection:
#Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
chart = createChart(timeSeriesDataset);
chartComposite = new MyChartComposite(this, parent, SWT.NONE, chart, true);
chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
selectionProvider = new GenericObjectSelectionProvider();
getSite().setSelectionProvider(selectionProvider);
// add information to the status line:
selectionProvider.addSelectionChangedListener(statusLineListener);
addDropSupport();// add D'n D support for dropping TimeSeries
}
protected JFreeChart createChart(TimeSeriesCollection ptimeSeriesDataset) {
JFreeChart vChart = ChartFactory.createTimeSeriesChart(null, "time", "values", ptimeSeriesDataset, true,
false, false);
vChart.setBackgroundPaint(Color.white);
XYPlot plot = vChart.getXYPlot();
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
// plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
plot.setRenderer(new /*OptimisedXYLineAndShapeRenderer()*/ StandardXYItemRendererFast());
XYItemRenderer renderer = plot.getRenderer();
renderer.setBaseToolTipGenerator(new MyXYSeriesToolTipGenerator());
renderer.setBaseItemLabelGenerator(new MyXYSeriesItemLabelGenerator());
renderer.setLegendItemLabelGenerator(new MyXYSeriesLegendItemLabelGenerator());
if (renderer instanceof XYLineAndShapeRenderer) {
XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) renderer;
r.setBaseShapesVisible(false);
r.setBaseShapesFilled(true);
}
SimpleDateFormat dateFormat = getDateFormatAbscissa();
if (dateFormat != null){
DateAxis axis = (DateAxis) plot.getDomainAxis();
axis.setDateFormatOverride(dateFormat);
}
return vChart;
}
My problem is that when too many variables are added to the chart (a TimeSeriesChart) the caption takes too much space and the graph disappears from the view:
ChartComposite with 2 series
ChartComposite many series
I tried to create a ScrollComposite to scroll in the ChartComposite and the result is a little better; but it only makes it possible to add more items in the caption before the graph disappears again:
ScrolledComposite scrollableChart = new ScrolledComposite(parent, SWT.BORDER|SWT.V_SCROLL);
chartComposite = new MyChartComposite(this, scrollableChart, SWT.NONE, chart, true);
//chartComposite = new MyChartComposite(this, parent, SWT.NONE, chart, true);
//chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
scrollableChart.setContent(chartComposite);
scrollableChart.setExpandVertical(true);
scrollableChart.setExpandHorizontal(true);
scrollableChart.setMinSize(ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH, ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH);
My question is: How to provide a real scrollbar to the ChartComposite in order to keep the graph when many series are plotted on the graph?
I was able to sync a slider to the XYSeries in SWT using a ChartComosite and Slider objects through the use of FormData. And every time I move the slider I capture that event and update the chart myself according to my needs.
My use case may be different than yours, but it's worth to take a look to my answer here.
If you have questions regarding my implementation, described in that answer, feel free to ask for details
After several unsuccessful tries, I decided to limit the number of LegendItem shown on the chart.
To change the legend items to display in the LegendTitle I used a slider. The most difficult part was to recreate the a new LegendTitle when using the slider; I am not sure that my solution is optimal or elegant but at least it is working.
To make this possible I needed to listen to series creation (ChartChangeEventType.DATASET_UPDATED) to refresh the LegendTitle and set slider values.
So here is the code:
public class MyExampleChartComposite extends ChartComposite {
// snip
/**
* A slider to choose the legend items to display
*/
private Slider legendSlider;
/**
* Number of legend items to display on the chart
*/
private final static int NUMBER_OF_LEGENDITEMS_TO_DISPLAY = 10;
private void createPartControl(Composite parent, int style) {
JFreeChart chart = createChart();
setChart(chart);
legendSlider = new Slider(parent, SWT.NONE|SWT.H_SCROLL);
legendSlider.addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent arg0) {
refreshLegend();
}
});
private JFreeChart createChart() {
chart.addChangeListener(new ChartChangeListener() {
#Override
public void chartChanged(ChartChangeEvent e) {
if (e.getType().equals(ChartChangeEventType.DATASET_UPDATED)) {
refreshLegend();
}
}
});
}
/**
* Refresh the the LegendItems and the slider,
* according to slider selection.
*/
public void refreshLegend() {
// Display LegendItems according to the selection
// display the 10 nearest legend item (current selected element included)
int begin = legendSlider.getSelection() - (NUMBER_OF_LEGENDITEMS_TO_DISPLAY/2);
int end = legendSlider.getSelection() + (NUMBER_OF_LEGENDITEMS_TO_DISPLAY/2 -1);
int seriesEndIndex = Math.max(getSeriesCount()-1, 0);
// if begin is less than 0
// set it to 0, and increase end to display 10 items
if (begin < 0) {
begin = 0;
end = NUMBER_OF_LEGENDITEMS_TO_DISPLAY - 1;
}
// if end is greater than the number of series plotted
// set it to the max possible value and increase begin to
// display 10 items
if (end > seriesEndIndex) {
end = seriesEndIndex;
begin = seriesEndIndex - (NUMBER_OF_LEGENDITEMS_TO_DISPLAY - 1);
}
end = Math.min(seriesEndIndex, end);
begin = Math.max(begin, 0);
// Refresh only if begin != end
if (end != begin) {
refreshLegendItems(begin, end);
refreshLegendSlider();
} else {
// in this case no more series are plotted on the chart
// clear legend
getChart().clearSubtitles();
}
}
/**
* Refresh the LegendTitle.
* Display only LegendItems between beginIndex and toIndex,
* to preserve space for the chart.
* #param beginIndex index of the {#link LegendItemCollection} used as the beginning of the new {#link LegendTitle}
* #param endIndex index of the {#link LegendItemCollection} used as the end of the new {#link LegendTitle}
*/
private void refreshLegendItems(int beginIndex, int endIndex) {
// Last 10 items
final LegendItemCollection result = new LegendItemCollection();
// get the renderer to retrieve legend items
XYPlot plot = getChart().getXYPlot();
XYItemRenderer renderer = plot.getRenderer();
// Number of series displayed on the chart
// Construct the legend
for (int i = beginIndex; i <= endIndex; i++) {
LegendItem item = renderer.getLegendItem(0, i);
result.add(item);
}
// Because the only way to create a new LegendTitle is to
// create a LegendItemSource first
LegendItemSource source = new LegendItemSource() {
LegendItemCollection lic = new LegendItemCollection();
{lic.addAll(result);}
public LegendItemCollection getLegendItems() {
return lic;
}
};
// clear previous legend title
getChart().clearSubtitles();
// Create the new LegendTitle and set its position
LegendTitle legend = new LegendTitle(source);
legend.setHorizontalAlignment(HorizontalAlignment.CENTER);
legend.setVerticalAlignment(VerticalAlignment.CENTER);
legend.setPosition(RectangleEdge.BOTTOM);
legend.setBorder(1.0, 1.0, 1.0, 1.0);
legend.setVisible(true);
// Add the LegendTitle to the graph
getChart().addLegend(legend);
}
/**
* Set values of the slider according to the number of series
* plotted on the graph
*/
private void refreshLegendSlider() {
int max = getSeriesCount() -1;
int selection = Math.min(legendSlider.getSelection(), max);
legendSlider.setValues(selection, 0, max, 1, 1, 1);
}
}

Resources