I have a barChart with overplot a lineChart, two different series
barChart.getData().addAll(series1);
lineChart.getData().addAll(series2);
My question: is it possible, and how to, clone barChart axis so to get the same settings for lineChart?
What I would like to have is a perfect axis settings overlapping.
Thanks
Edit: at this link
axis clone method
I see there is a clone method Methods inherited from class java.lang.Object
How can I use this method?
Thanks all
Edit2: below is a sample class to show my problem: it plots a line chart and by clicking the Add serie button it adds a scatter chart.
As it plots yellow dots both axis overlap, but just move chart left/right or upper/dow by left mouse click and drag anywhere on the chart and axis goes out of sync.
Same problem for axis expand/compress by left mouse click and drag on X or Y axis
import java.util.Set;
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.chart.*;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
public class RescalingSeries extends Application {
StackPane mainGraphStackPane = null;
Button btnAdd;
BorderPane pane;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
protected static Axis _duplicateAxis(Axis axis, Axis result) {
result.setAnimated(axis.animatedProperty().get());
result.setAutoRanging(axis.isAutoRanging());
result.setLabel(axis.getLabel());
result.setSide(axis.getSide());
result.setTickLabelFill(axis.getTickLabelFill());
result.setTickLabelFont(axis.getTickLabelFont());
result.setTickLabelGap(axis.getTickLabelGap());
result.setTickLength(axis.getTickLength());
return result;
}
protected static ValueAxis _duplicateValueAxis(ValueAxis axis, ValueAxis result) {
_duplicateAxis(axis, result);
result.setLowerBound(axis.getLowerBound());
result.setUpperBound(axis.getUpperBound());
result.setMinorTickCount(axis.getMinorTickCount());
result.setMinorTickLength(axis.getMinorTickLength());
result.setTickLabelFormatter(axis.getTickLabelFormatter());
return result;
}
/**
* Duplicate a number axis.
* #param axis The source axis.
* #return A {#code NumberAxis}, never {#code null}.
*/
public static NumberAxis duplicateNumberAxis(NumberAxis axis) {
NumberAxis result = new NumberAxis();
_duplicateValueAxis(axis, result);
result.setTickUnit(axis.getTickUnit());
result.setForceZeroInRange(axis.isForceZeroInRange());
return result;
}
/**
* Duplicate a category axis.
* #param axis The source axis.
* #return A {#code CategoryAxis}, never {#code null}.
*/
public static CategoryAxis duplicateCategoryAxis(CategoryAxis axis) {
CategoryAxis result = new CategoryAxis(axis.getCategories());
_duplicateAxis(axis, result);
result.setStartMargin(axis.getStartMargin());
result.setEndMargin(axis.getEndMargin());
result.setGapStartAndEnd(axis.gapStartAndEndProperty().get());
return result;
}
#Override
public void start(Stage stage) {
final NumberAxis xAxisLC = new NumberAxis(1, 12, 1);
final NumberAxis yAxisLC = new NumberAxis(0.53000, 0.53910, 0.0005);
yAxisLC.setSide(Side.RIGHT);
yAxisLC.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxisLC) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart = new LineChart<>(xAxisLC, yAxisLC);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(true);
lineChart.setLegendVisible(false);
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
series1.getData().add(new XYChart.Data(11, 0.530123));
series1.getData().add(new XYChart.Data(12, 0.531035));
pane = new BorderPane();
pane.setCenter(lineChart);
mainGraphStackPane = new StackPane();
mainGraphStackPane.getChildren().add(pane);
Scene scene = new Scene(mainGraphStackPane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
Group root = new Group();
btnAdd = new Button();
btnAdd.setText("Add serie");
root.getChildren().add(btnAdd);
pane.getChildren().add(root);
btnAdd.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
NumberAxis xAxisBC = duplicateNumberAxis(xAxisLC);
NumberAxis yAxisBC = duplicateNumberAxis(yAxisLC);
ScatterChart<Number, Number> scatterChart = new ScatterChart<>(xAxisBC, yAxisBC);
scatterChart.setAlternativeRowFillVisible(false);
scatterChart.setAnimated(true);
scatterChart.setLegendVisible(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data(1, 0.53185));
series2.getData().add(new XYChart.Data(2, 0.532235));
series2.getData().add(new XYChart.Data(3, 0.53234));
series2.getData().add(new XYChart.Data(4, 0.538765));
series2.getData().add(new XYChart.Data(5, 0.53442));
series2.getData().add(new XYChart.Data(6, 0.534658));
series2.getData().add(new XYChart.Data(7, 0.53023));
series2.getData().add(new XYChart.Data(8, 0.53001));
series2.getData().add(new XYChart.Data(9, 0.53589));
series2.getData().add(new XYChart.Data(10, 0.53476));
series2.getData().add(new XYChart.Data(11, 0.530123));
series2.getData().add(new XYChart.Data(12, 0.531035));
scatterChart.getData().addAll(series2);
Set<Node> chartNode = scatterChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent;");
}
chartNode = lineChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent");
}
mainGraphStackPane.getChildren().add(scatterChart);
xAxisBC.lowerBoundProperty().bind(xAxisLC.lowerBoundProperty());
yAxisBC.lowerBoundProperty().bind(yAxisLC.lowerBoundProperty());
}
});
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
boolean XScaling=false;
boolean YScaling=false;
if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED || mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED ){
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());
double newXlower=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double newYlower=yAxis.getLowerBound(), newYupper=yAxis.getUpperBound();
double xAxisShift = xAxis.localToScene(0, 0).getX();
double yAxisShift = yAxis.localToScene(0, 0).getY();
double yAxisStep=yAxis.getHeight()/(yAxis.getUpperBound()-yAxis.getLowerBound());
double CurrentPrice=yAxis.getUpperBound()-((mouseEvent.getY()-yAxisShift)/yAxisStep);
double Delta=0.3;
if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getX()<xAxisShift+yAxis.getHeight() && mouseEvent.getY()<yAxisShift+yAxis.getHeight() && (XScaling==false || YScaling==false)){
//==================================================== X-Axis Moving ==================================
if(rectinitX.get() < mouseEvent.getX()){
newXlower=xAxis.getLowerBound()-Delta;
newXupper=xAxis.getUpperBound()-Delta;
}
else if(rectinitX.get() > mouseEvent.getX()){
newXlower=xAxis.getLowerBound()+Delta;
newXupper=xAxis.getUpperBound()+Delta;
}
xAxis.setLowerBound( newXlower );
xAxis.setUpperBound( newXupper );
//===================================================== Y-Axis Moving ====================================
if(rectinitY.get() < mouseEvent.getY()){
newYlower=yAxis.getLowerBound()+Delta/1000;
newYupper=yAxis.getUpperBound()+Delta/1000;
}
else if(rectinitY.get() > mouseEvent.getY()){
newYlower=yAxis.getLowerBound()-Delta/1000;
newYupper=yAxis.getUpperBound()-Delta/1000;
}
yAxis.setLowerBound(newYlower);
yAxis.setUpperBound(newYupper);
}
//----------------------------- Re-Scale the X-Axis when dragging below it ---------------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getY()>yAxisShift+yAxis.getHeight() ){
if(rectinitX.get() < mouseEvent.getX()){
newXlower=xAxis.getLowerBound()+Delta;
newXupper=xAxis.getUpperBound()-Delta;
}
else if(rectinitX.get() > mouseEvent.getX()){
newXlower=xAxis.getLowerBound()-Delta;
newXupper=xAxis.getUpperBound()+Delta;
}
xAxis.setLowerBound( newXlower );
xAxis.setUpperBound( newXupper );
}
//--------------------------------- Re-Scale the Y-Axis when dragging to the left of it --------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getX()> (xAxisShift + xAxis.getWidth())){
if(rectinitY.get() < mouseEvent.getY()){
newYlower=yAxis.getLowerBound()-Delta/1000;
newYupper=yAxis.getUpperBound()+Delta/1000;
}
else if(rectinitY.get() > mouseEvent.getY()){
newYlower=yAxis.getLowerBound()+Delta/1000;
newYupper=yAxis.getUpperBound()-Delta/1000;
}
yAxis.setLowerBound(newYlower);
yAxis.setUpperBound(newYupper);
}
rectinitX.set(mouseEvent.getX());
rectinitY.set(mouseEvent.getY());
if(mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED && mouseEvent.getY()>yAxisShift && mouseEvent.getY()<yAxisShift+yAxis.getHeight() && mouseEvent.getX()>xAxisShift && mouseEvent.getX()<xAxisShift+xAxis.getWidth()){
double XX=((mouseEvent.getX() - xAxisShift) / Tgap) + xAxis.getLowerBound();
double YY=CurrentPrice;
series1.setName(String.format("%.2g%n",XX) + ", " + String.format("%.4g%n",YY));
}
}
}
};
public static void main(String[] args) {
launch(args);
}
}
The
Chart, Axis, and Series
objects are not clone-able. For example I needed to include charts in two scenes. The only means to do so was to start with the original data and create every one of those elements twice.
Related
With a left mouse click then mouse moved, a line is drawned on this line chart, and also on axis.
I would like to draw line only on chart so that it does not overlap x or y axis. How to accomplish this?
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
public class LinesEdit extends Application {
Path path;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setSide(Side.RIGHT);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 4));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 5));
series1.getData().add(new XYChart.Data("May", 6));
series1.getData().add(new XYChart.Data("Jun", 8));
series1.getData().add(new XYChart.Data("Jul", 12));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 11));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler( bp );
bp.setOnMouseClicked( mh );
bp.setOnMouseMoved( mh );
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
scene.setOnMouseDragged(mh);
scene.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
stage.show();
}
class MouseHandler implements EventHandler< MouseEvent > {
private boolean gotFirst = false;
private Line line;
private Pane pane;
private double x1, y1, x2, y2;
private LineHandler lineHandler;
public MouseHandler( Pane pane ) {
this.pane = pane;
lineHandler = new LineHandler(pane);
}
class LineHandler implements EventHandler< MouseEvent > {
double x, y;
Pane pane;
public LineHandler(Pane pane){
this.pane = pane;
}
#Override
public void handle( MouseEvent e ) {
Line l = (Line) e.getSource();
// remove line on right click
if( e.getEventType() == MouseEvent.MOUSE_PRESSED
&& e.isSecondaryButtonDown() ) {
pane.getChildren().remove( l );
} else if( e.getEventType() == MouseEvent.MOUSE_DRAGGED
&& e.isPrimaryButtonDown() ) {
double tx = e.getX();
double ty = e.getY();
double dx = tx - x;
double dy = ty - y;
l.setStartX( l.getStartX() + dx );
l.setStartY( l.getStartY() + dy );
l.setEndX( l.getEndX() + dx );
l.setEndY( l.getEndY() + dy );
x = tx;
y = ty;
} else if( e.getEventType() == MouseEvent.MOUSE_ENTERED ) {
// just to show that the line is selected
x = e.getX();
y = e.getY();
l.setStroke( Color.RED );
} else if( e.getEventType() == MouseEvent.MOUSE_EXITED ) {
l.setStroke( Color.BLACK );
}
// should not pass event to the parent
e.consume();
}
}
#Override
public void handle( MouseEvent event ) {
if( event.getEventType() == MouseEvent.MOUSE_CLICKED ) {
if( !gotFirst ) {
x1 = x2 = event.getX();
y1 = y2 = event.getY();
line = new Line( x1, y1, x2, y2 );
pane.getChildren().add( line );
gotFirst = true;
}
else {
line.setOnMouseEntered( lineHandler );
line.setOnMouseExited( lineHandler );
line.setOnMouseDragged( lineHandler );
line.setOnMousePressed( lineHandler );
// to consume the event
line.setOnMouseClicked( lineHandler );
line.setOnMouseReleased( lineHandler );
line = null;
gotFirst = false;
}
}
else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
}
}
}
}
}
The first thing to know is that many JavaFX UI-Element like charts consist of many underlying children. The part where you want to draw is in fact a Region that is part of a Pane that is child of the LineChart. I really recommend you to use ScenicView, because it shows exactly how your scene graph (including built-in UI-Components) looks like.
Back to your problem: your listeners should only apply to the Region which shows the actual representation of the data. That Region ends exactly where the x and y axis are. The following code will get you that Region and make it the target for your listeners:
//your previous code in start()...
Pane p = (Pane) lineChart.getChildrenUnmodifiable().get(1);
Region r = (Region) p.getChildren().get(0);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler(r);
r.setOnMouseClicked(mh);
r.setOnMouseMoved(mh);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
r.setOnMouseDragged(mh);
r.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
//the following code.....
The next steps are:
1. rewrite your handler methods, so that they accept a `Region` object as parameter
2. rewrite a bit of your line-setting code. Background: you cannot add objects into a `Region` because you cannot access the writable `List` of childrens. So you have to put the lines into the `Pane` object which holds the `Region`. Because every JavaFX UI-Element has its own coordinate system, you have to calculate the offset between the `Pane` and the `Region`, because the `Pane` is a bit larger. If you don't do this, your line will be drawn slightly above the mousepointer. You can get the width and height of a `Pane` and/or `Region` by calling `xyz.getBoundsInLocal().getWidth()/height()`.
UPDATE: Full Solution as requested
As requested in the comments i will show one way to solve this problem. Have a look at your GUI in this picture. It shows all graphical elements in ScenicView. As you can see, the Pane is bigger than the inner Region. Important for us is to know that the origin of all coordinate systems in JavaFX start at the upper left corner of an element. In this scenario, you have to add a line to the Pane, but in respect to the borders of the Region. In the code-snippet i showed earlier i added all of your listeners to the region, which means we get the mouse coordinates inside the coordinate system of the region. Now we have to "translate" or better "transform" these coordinates (the points you wish to set the start or endpoints of the line) into the coordinate system of the Pane (the place the line is actually placed, read above why), because we want the line to start exactly where our mouse is. There is a method you can call to get a transformation matrix: r.getLocalToParentTransform(). We need this matrix because we have to get the exact values for the x and y translation that is applied to the Region (you can see that the Region is approximatly 10 pixels moved from the Panes origin in both x and y axes)
I wrote a simple method for getting the x and y translation between the Region and the Pane: getCoordDiff(Region r, Pane p).
The rest of start() method remains unchanged (but with the changes i wrote earlier). But the handle() methods of MouseHandler and LineHandler have to be modified.
public class LinesEdit extends Application
{
Path path;
public double[] getCoordDiff(Region r, Pane p)
{
//Acquires transformation matrix and returns x and y offset/translation from parent
double[] diffs =
{ r.getLocalToParentTransform().getTx(), r.getLocalToParentTransform().getTy() };
return diffs;
}
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setSide(Side.RIGHT);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis)
{
#Override
public String toString(Number object)
{
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 4));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 5));
series1.getData().add(new XYChart.Data("May", 6));
series1.getData().add(new XYChart.Data("Jun", 8));
series1.getData().add(new XYChart.Data("Jul", 12));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 11));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
Pane p = (Pane) lineChart.getChildrenUnmodifiable().get(1);
Region r = (Region) p.getChildren().get(0);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler(r);
r.setOnMouseClicked(mh);
r.setOnMouseMoved(mh);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
r.setOnMouseDragged(mh);
r.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
ScenicView.show(scene);
stage.show();
}
class MouseHandler implements EventHandler<MouseEvent>
{
private boolean gotFirst = false;
private Line line;
private Region reg;
private double x1, y1, x2, y2;
private LineHandler lineHandler;
public MouseHandler(Region reg)
{
this.reg = reg;
lineHandler = new LineHandler(reg);
}
class LineHandler implements EventHandler<MouseEvent>
{
double x, y;
Region reg;
public LineHandler(Region reg)
{
this.reg = reg;
}
#Override
public void handle(MouseEvent e)
{
Line l = (Line) e.getSource();
l.setStrokeWidth(3);
// remove line on right click
if (e.getEventType() == MouseEvent.MOUSE_PRESSED && e.isSecondaryButtonDown())
{
((Pane) reg.getParent()).getChildren().remove(l);
}
else if (e.getEventType() == MouseEvent.MOUSE_DRAGGED && e.isPrimaryButtonDown())
{
double tx = e.getX();
double ty = e.getY();
double dx = tx - x;
double dy = ty - y;
l.setStartX(l.getStartX() + dx);
l.setStartY(l.getStartY() + dy);
l.setEndX(l.getEndX() + dx);
l.setEndY(l.getEndY() + dy);
x = tx;
y = ty;
}
else if (e.getEventType() == MouseEvent.MOUSE_ENTERED)
{
// just to show that the line is selected
x = e.getX();
y = e.getY();
l.setStroke(Color.RED);
}
else if (e.getEventType() == MouseEvent.MOUSE_EXITED)
{
l.setStroke(Color.BLACK);
}
// should not pass event to the parent
e.consume();
}
}
#Override
public void handle(MouseEvent event)
{
if (event.getEventType() == MouseEvent.MOUSE_CLICKED)
{
double[] diff = getCoordDiff(reg, (Pane) reg.getParent());
if (!gotFirst)
{
//add translation to start/endcoordinates
x1 = x2 = event.getX() + diff[0];
y1 = y2 = event.getY() + diff[1];
line = new Line(x1, y1, x2, y2);
line.setStrokeWidth(3);
((Pane) reg.getParent()).getChildren().add(line);
gotFirst = true;
line.setOnMouseClicked(new EventHandler<Event>()
{
#Override
public void handle(Event arg0)
{
line.setOnMouseEntered(lineHandler);
line.setOnMouseExited(lineHandler);
line.setOnMouseDragged(lineHandler);
line.setOnMousePressed(lineHandler);
// to consume the event
line.setOnMouseClicked(lineHandler);
line.setOnMouseReleased(lineHandler);
line = null;
gotFirst = false;
}
});
}
}
else
{
if (line != null)
{
double[] diff = getCoordDiff(reg, (Pane) reg.getParent());
//add translation to end coordinates
x2 = event.getX() + diff[0];
y2 = event.getY() + diff[1];
// update line
line.setEndX(x2);
line.setEndY(y2);
}
}
}
}
}
You can see the parts where i add the translation values to the lines start and endpoints. This is needed so that line really starts and ends at the points where your mouse is. I moved your code that was executed if gotFirst == true, because it prevented the user to place the line (so that it doesn't follow the cursor). Background: your cursor is now always (pixel perfect) at the end of the line , which doesn't have a 'Listener' at the moment you place it the first time. That missing listener prevents the MouseEvent-click from going to the Region. In short: the "Click" event is now always done on the line itself, thats why we need a listener before the line is finally placed.
Remaining bugs: a line cannot be placed on the yellow lines of the graph. Thats because the click-event is not triggerd on the lines. I might fix that bug at a later time, or you try yourself.
This question already has answers here:
How to combine scatter chart with line chart to show line of regression? JavaFX
(4 answers)
Closed 8 years ago.
This code plots a LineChart and by clicking the "Add serie" button it adds a ScatterChart
It seems all ok but just move scene left/right or up/down (left mouse click and drag) both X, Y axis and charts are displaced.
How to fix this code in order to always get axis overlap as well as both charts?
Thanks.
import java.util.Set;
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.chart.*;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
public class RescalingSeries extends Application {
StackPane mainGraphStackPane = null;
Button btnAdd;
BorderPane pane;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
protected static Axis _duplicateAxis(Axis axis, Axis result) {
result.setAnimated(axis.animatedProperty().get());
result.setAutoRanging(axis.isAutoRanging());
result.setLabel(axis.getLabel());
result.setSide(axis.getSide());
result.setTickLabelFill(axis.getTickLabelFill());
result.setTickLabelFont(axis.getTickLabelFont());
result.setTickLabelGap(axis.getTickLabelGap());
result.setTickLength(axis.getTickLength());
return result;
}
protected static ValueAxis _duplicateValueAxis(ValueAxis axis, ValueAxis result) {
_duplicateAxis(axis, result);
result.setLowerBound(axis.getLowerBound());
result.setUpperBound(axis.getUpperBound());
result.setMinorTickCount(axis.getMinorTickCount());
result.setMinorTickLength(axis.getMinorTickLength());
result.setTickLabelFormatter(axis.getTickLabelFormatter());
return result;
}
/**
* Duplicate a number axis.
* #param axis The source axis.
* #return A {#code NumberAxis}, never {#code null}.
*/
public static NumberAxis duplicateNumberAxis(NumberAxis axis) {
NumberAxis result = new NumberAxis();
_duplicateValueAxis(axis, result);
result.setTickUnit(axis.getTickUnit());
result.setForceZeroInRange(axis.isForceZeroInRange());
return result;
}
/**
* Duplicate a category axis.
* #param axis The source axis.
* #return A {#code CategoryAxis}, never {#code null}.
*/
public static CategoryAxis duplicateCategoryAxis(CategoryAxis axis) {
CategoryAxis result = new CategoryAxis(axis.getCategories());
_duplicateAxis(axis, result);
result.setStartMargin(axis.getStartMargin());
result.setEndMargin(axis.getEndMargin());
result.setGapStartAndEnd(axis.gapStartAndEndProperty().get());
return result;
}
#Override
public void start(Stage stage) {
final NumberAxis xAxisLC = new NumberAxis(1, 12, 1);
final NumberAxis yAxisLC = new NumberAxis(0.53000, 0.53910, 0.0005);
yAxisLC.setSide(Side.RIGHT);
yAxisLC.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxisLC) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart = new LineChart<>(xAxisLC, yAxisLC);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(true);
lineChart.setLegendVisible(false);
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
series1.getData().add(new XYChart.Data(11, 0.530123));
series1.getData().add(new XYChart.Data(12, 0.531035));
pane = new BorderPane();
pane.setCenter(lineChart);
mainGraphStackPane = new StackPane();
mainGraphStackPane.getChildren().add(pane);
Scene scene = new Scene(mainGraphStackPane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
Group root = new Group();
btnAdd = new Button();
btnAdd.setText("Add serie");
root.getChildren().add(btnAdd);
pane.getChildren().add(root);
btnAdd.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
NumberAxis xAxisBC = duplicateNumberAxis(xAxisLC);
NumberAxis yAxisBC = duplicateNumberAxis(yAxisLC);
ScatterChart<Number, Number> scatterChart = new ScatterChart<>(xAxisBC, yAxisBC);
scatterChart.setAlternativeRowFillVisible(false);
scatterChart.setAnimated(true);
scatterChart.setLegendVisible(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data(1, 0.53185));
series2.getData().add(new XYChart.Data(2, 0.532235));
series2.getData().add(new XYChart.Data(3, 0.53234));
series2.getData().add(new XYChart.Data(4, 0.538765));
series2.getData().add(new XYChart.Data(5, 0.53442));
series2.getData().add(new XYChart.Data(6, 0.534658));
series2.getData().add(new XYChart.Data(7, 0.53023));
series2.getData().add(new XYChart.Data(8, 0.53001));
series2.getData().add(new XYChart.Data(9, 0.53589));
series2.getData().add(new XYChart.Data(10, 0.53476));
series2.getData().add(new XYChart.Data(11, 0.530123));
series2.getData().add(new XYChart.Data(12, 0.531035));
scatterChart.getData().addAll(series2);
Set<Node> chartNode = scatterChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent;");
}
chartNode = lineChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent");
}
mainGraphStackPane.getChildren().add(scatterChart);
xAxisBC.lowerBoundProperty().bind(xAxisLC.lowerBoundProperty());
yAxisBC.lowerBoundProperty().bind(yAxisLC.lowerBoundProperty());
}
});
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
boolean XScaling=false;
boolean YScaling=false;
if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED || mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED ){
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());
double newXlower=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double newYlower=yAxis.getLowerBound(), newYupper=yAxis.getUpperBound();
double xAxisShift = xAxis.localToScene(0, 0).getX();
double yAxisShift = yAxis.localToScene(0, 0).getY();
double yAxisStep=yAxis.getHeight()/(yAxis.getUpperBound()-yAxis.getLowerBound());
double CurrentPrice=yAxis.getUpperBound()-((mouseEvent.getY()-yAxisShift)/yAxisStep);
double Delta=0.3;
if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getX()<xAxisShift+yAxis.getHeight() && mouseEvent.getY()<yAxisShift+yAxis.getHeight() && (XScaling==false || YScaling==false)){
//==================================================== X-Axis Moving ==================================
if(rectinitX.get() < mouseEvent.getX()){
newXlower=xAxis.getLowerBound()-Delta;
newXupper=xAxis.getUpperBound()-Delta;
}
else if(rectinitX.get() > mouseEvent.getX()){
newXlower=xAxis.getLowerBound()+Delta;
newXupper=xAxis.getUpperBound()+Delta;
}
xAxis.setLowerBound( newXlower );
xAxis.setUpperBound( newXupper );
//===================================================== Y-Axis Moving ====================================
if(rectinitY.get() < mouseEvent.getY()){
newYlower=yAxis.getLowerBound()+Delta/1000;
newYupper=yAxis.getUpperBound()+Delta/1000;
}
else if(rectinitY.get() > mouseEvent.getY()){
newYlower=yAxis.getLowerBound()-Delta/1000;
newYupper=yAxis.getUpperBound()-Delta/1000;
}
yAxis.setLowerBound(newYlower);
yAxis.setUpperBound(newYupper);
}
//----------------------------- Re-Scale the X-Axis when dragging below it ---------------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getY()>yAxisShift+yAxis.getHeight() ){
if(rectinitX.get() < mouseEvent.getX()){
newXlower=xAxis.getLowerBound()+Delta;
newXupper=xAxis.getUpperBound()-Delta;
}
else if(rectinitX.get() > mouseEvent.getX()){
newXlower=xAxis.getLowerBound()-Delta;
newXupper=xAxis.getUpperBound()+Delta;
}
xAxis.setLowerBound( newXlower );
xAxis.setUpperBound( newXupper );
}
//--------------------------------- Re-Scale the Y-Axis when dragging to the left of it --------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getX()> (xAxisShift + xAxis.getWidth())){
if(rectinitY.get() < mouseEvent.getY()){
newYlower=yAxis.getLowerBound()-Delta/1000;
newYupper=yAxis.getUpperBound()+Delta/1000;
}
else if(rectinitY.get() > mouseEvent.getY()){
newYlower=yAxis.getLowerBound()+Delta/1000;
newYupper=yAxis.getUpperBound()-Delta/1000;
}
yAxis.setLowerBound(newYlower);
yAxis.setUpperBound(newYupper);
}
rectinitX.set(mouseEvent.getX());
rectinitY.set(mouseEvent.getY());
if(mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED && mouseEvent.getY()>yAxisShift && mouseEvent.getY()<yAxisShift+yAxis.getHeight() && mouseEvent.getX()>xAxisShift && mouseEvent.getX()<xAxisShift+xAxis.getWidth()){
double XX=((mouseEvent.getX() - xAxisShift) / Tgap) + xAxis.getLowerBound();
double YY=CurrentPrice;
series1.setName(String.format("%.2g%n",XX) + ", " + String.format("%.4g%n",YY));
}
}
}
};
public static void main(String[] args) {
launch(args);
}
}
Found a solution: I missed this part
xAxisBC.upperBoundProperty().bind(xAxisLC.upperBoundProperty());
yAxisBC.upperBoundProperty().bind(yAxisLC.upperBoundProperty());
In my code below I would like to add two buttons and by clicking on a button("Remove") it will remove one by one plotted data,and plot it back by clicking on the button("Add") such as the examples:
Full data plotted by running the class
Now by a single click on a Remove button last data point disappear
another click and again last data point disappear, and so on
The inverse operation would be performed by clicking on "Add" button: each click will add back a data point
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
public class XYMove extends Application {
BorderPane pane;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectX = new SimpleDoubleProperty();
SimpleDoubleProperty rectY = new SimpleDoubleProperty();
Button button1 = new Button("Remove");
Button button2 = new Button("Add");
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis(12, 20, 1);
double max = 12;
double min = 3;
max *= (1+((double)3/100));
min *= (1-((double)3/100));
final NumberAxis yAxis = new NumberAxis(min, max, 1);
xAxis.setAnimated(false);
yAxis.setAnimated(false);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%2.0f", object);
}
});
final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(false);
lineChart.setLegendVisible(false);
series1.getData().add(new XYChart.Data(1, 3));
series1.getData().add(new XYChart.Data(2, 8));
series1.getData().add(new XYChart.Data(3, 6));
series1.getData().add(new XYChart.Data(4, 7));
series1.getData().add(new XYChart.Data(5, 5));
series1.getData().add(new XYChart.Data(6, 6));
series1.getData().add(new XYChart.Data(7, 4));
series1.getData().add(new XYChart.Data(8, 7));
series1.getData().add(new XYChart.Data(9, 6));
series1.getData().add(new XYChart.Data(10, 7));
series1.getData().add(new XYChart.Data(11, 6));
series1.getData().add(new XYChart.Data(12, 7));
series1.getData().add(new XYChart.Data(13, 6));
series1.getData().add(new XYChart.Data(14, 12));
series1.getData().add(new XYChart.Data(15, 10));
series1.getData().add(new XYChart.Data(16, 11));
series1.getData().add(new XYChart.Data(17, 9));
series1.getData().add(new XYChart.Data(18, 10));
pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
rectinitX.set(mouseEvent.getX());
}
else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED || mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED) {
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());
double newXlower=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double Delta=0.3;
if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED){
if(rectinitX.get() < mouseEvent.getX()&& newXlower >= 0){
newXlower=xAxis.getLowerBound()-Delta;
newXupper=xAxis.getUpperBound()-Delta;
}
else if(rectinitX.get() > mouseEvent.getX()&& newXupper <= 22){
newXlower=xAxis.getLowerBound()+Delta;
newXupper=xAxis.getUpperBound()+Delta;
}
xAxis.setLowerBound( newXlower );
xAxis.setUpperBound( newXupper );
}
rectinitX.set(mouseEvent.getX());
}
}
};
public static void main(String[] args) {
launch(args);
}
}
Thanks all.
Edit: I have been able to solve this issue, thanks all
This code plots a LineChart upper pane and a bar Chart in lower pane.
There is also a vertical line plotted left/right X move with pointer.
I would like to extend this vertical line also to the lower subpane, such as the attached picture
How to accomplish this?
Thanks.
Here is the code
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.control.SplitPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineBuilder;
import javafx.stage.Stage;
public class XyChartInSplitOnlyCursor extends Application {
SplitPane splitPane1 = null;
BorderPane pane1;
BorderPane pane2;
Line LV;
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
#Override
public void start(Stage stage) {
stage.setTitle("Lines plot");
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);
xAxis.setAnimated(false);
xAxis.setScaleX(0);
yAxis.setAnimated(false);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart1 = new LineChart<Number, Number>(xAxis, yAxis);
lineChart1.setCreateSymbols(false);
lineChart1.setAlternativeRowFillVisible(false);
lineChart1.setAnimated(false);
lineChart1.setLegendVisible(false);
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
series1.getData().add(new XYChart.Data(11, 0.530123));
series1.getData().add(new XYChart.Data(12, 0.531035));
lineChart1.getData().addAll(series1);
pane1 = new BorderPane();
pane1.setCenter(lineChart1);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(pane1);
splitPane1.setDividerPosition(0, 1);
final CategoryAxis xAxis2 = new CategoryAxis();
final NumberAxis yAxis2 = new NumberAxis();
yAxis2.setTickUnit(1);
yAxis2.setPrefWidth(35);
yAxis2.setMinorTickCount(10);
yAxis2.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis2){
#Override
public String toString(Number object){
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final BarChart<String, Number>BarChart = new BarChart<String, Number>(xAxis2, yAxis2);
BarChart.setAlternativeRowFillVisible(false);
BarChart.setLegendVisible(false);
BarChart.setAnimated(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data("Jan", 1));
series2.getData().add(new XYChart.Data("Feb", 3));
series2.getData().add(new XYChart.Data("Mar", 1.5));
series2.getData().add(new XYChart.Data("Apr", 3));
series2.getData().add(new XYChart.Data("May", 4.5));
series2.getData().add(new XYChart.Data("Jun", 5));
series2.getData().add(new XYChart.Data("Jul", 4));
series2.getData().add(new XYChart.Data("Aug", 8));
series2.getData().add(new XYChart.Data("Sep", 16.5));
series2.getData().add(new XYChart.Data("Oct", 13.9));
series2.getData().add(new XYChart.Data("Nov", 17));
series2.getData().add(new XYChart.Data("Dec", 20));
BarChart.getData().addAll(series2);
pane2 = new BorderPane();
pane2.setCenter(BarChart);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
splitPane1.getItems().addAll(pane2);
ObservableList<SplitPane.Divider> splitDiv = splitPane1.getDividers();
percSplit = 1/(double)(splitDiv.size()+1);
for (int i = 0; i< splitDiv.size(); i++) {
splitPane1.setDividerPosition(i, percSplit);
percSplit += 1/(double)(splitDiv.size()+1);
}
}
});
Scene scene = new Scene(splitPane1, 800, 600);
stage.setScene(scene);
pane1.setOnMouseMoved(mouseHandler);
LV=LineBuilder.create()
.startX(0)
.startY(0)
.endX(10)
.endY(.535)
.strokeWidth(1)
.stroke(Color.BLACK)
.build();
pane1.getChildren().add(LV);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED) {
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane1.getCenter();
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double newXlower=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double xAxisShift = getSceneShift(xAxis);
double yAxisShift = getSceneShift(yAxis);
if(mouseEvent.getX()>xAxisShift && mouseEvent.getX()<xAxisShift+xAxis.getWidth()){
LV.setStartX(mouseEvent.getX());
LV.setStartY(yAxisShift);
LV.setEndX(mouseEvent.getX());
LV.setEndY(yAxisShift+yAxis.getHeight());
}
}
}
};
private static double getSceneShift(Node node) {
double shift = 0;
do {
shift += node.getLayoutX();
node = node.getParent();
} while (node != null);
return shift;
}
public static void main(String[] args) {
launch(args);
}
}
Edit: added picture after jewelsea modify
Don't add the line to the pane in the SplitPane, instead create a Group which layers the line over the SplitPane.
Scene scene = new Scene(new Group(splitPane1, line), 800, 600);
If you want the Scene resizable, you can use a StackPane instead of a Group and bind the lines length to the scene height to get it to fill the Scene.
Here is a sample of applying using a StackPane and binding to extend the line in a resizable scene. Note, similarly to the originally posted code, the code is explicitly coded to only move the line when the mouse is inside the plot area of the top chart.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.control.SplitPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineBuilder;
import javafx.stage.Stage;
public class XyChartInSplitOnlyCursor extends Application {
SplitPane splitPane1 = null;
BorderPane pane1;
BorderPane pane2;
Line LV1, LV2;
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
#Override
public void start(Stage stage) {
stage.setTitle("Lines plot");
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);
xAxis.setAnimated(false);
xAxis.setScaleX(0);
yAxis.setAnimated(false);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart1 = new LineChart<Number, Number>(xAxis, yAxis);
lineChart1.setCreateSymbols(false);
lineChart1.setAlternativeRowFillVisible(false);
lineChart1.setAnimated(false);
lineChart1.setLegendVisible(false);
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
series1.getData().add(new XYChart.Data(11, 0.530123));
series1.getData().add(new XYChart.Data(12, 0.531035));
lineChart1.getData().addAll(series1);
pane1 = new BorderPane();
pane1.setCenter(lineChart1);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(pane1);
splitPane1.setDividerPosition(0, 1);
final CategoryAxis xAxis2 = new CategoryAxis();
final NumberAxis yAxis2 = new NumberAxis();
yAxis2.setTickUnit(1);
yAxis2.setPrefWidth(35);
yAxis2.setMinorTickCount(10);
yAxis2.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis2) {
#Override
public String toString(Number object) {
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final BarChart<String, Number> BarChart = new BarChart<String, Number>(xAxis2, yAxis2);
BarChart.setAlternativeRowFillVisible(false);
BarChart.setLegendVisible(false);
BarChart.setAnimated(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data("Jan", 1));
series2.getData().add(new XYChart.Data("Feb", 3));
series2.getData().add(new XYChart.Data("Mar", 1.5));
series2.getData().add(new XYChart.Data("Apr", 3));
series2.getData().add(new XYChart.Data("May", 4.5));
series2.getData().add(new XYChart.Data("Jun", 5));
series2.getData().add(new XYChart.Data("Jul", 4));
series2.getData().add(new XYChart.Data("Aug", 8));
series2.getData().add(new XYChart.Data("Sep", 16.5));
series2.getData().add(new XYChart.Data("Oct", 13.9));
series2.getData().add(new XYChart.Data("Nov", 17));
series2.getData().add(new XYChart.Data("Dec", 20));
BarChart.getData().addAll(series2);
pane2 = new BorderPane();
pane2.setCenter(BarChart);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
splitPane1.getItems().addAll(pane2);
ObservableList<SplitPane.Divider> splitDiv = splitPane1.getDividers();
percSplit = 1 / (double) (splitDiv.size() + 1);
for (int i = 0; i < splitDiv.size(); i++) {
splitPane1.setDividerPosition(i, percSplit);
percSplit += 1 / (double) (splitDiv.size() + 1);
}
}
});
LV1 = LineBuilder.create()
.strokeWidth(2)
.stroke(Color.FORESTGREEN)
.build();
StackPane stack = new StackPane();
Pane glassPane = new Pane();
glassPane.getChildren().add(LV1);
glassPane.minWidthProperty().bind(splitPane1.widthProperty());
glassPane.minHeightProperty().bind(splitPane1.heightProperty());
glassPane.setMouseTransparent(true);
stack.getChildren().addAll(splitPane1, glassPane);
Scene scene = new Scene(stack, 800, 600);
LV1.endYProperty().bind(scene.heightProperty());
stage.setScene(scene);
pane1.setOnMouseMoved(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
XYChart<Number, Number> chart1 = (XYChart<Number, Number>) pane1.getCenter();
plotLine(chart1, LV1, mouseEvent.getX() + 1);
}
};
private void plotLine(XYChart<Number, Number> chart, Line line, double x) {
Axis xAxis = chart.getXAxis(), yAxis = chart.getYAxis();
final double min = getSceneShift(xAxis);
final double max = min + xAxis.getWidth();
boolean setCrosshair = false;
if (x > min && x < min + xAxis.getWidth()) {
LV1.setStartX(x); LV1.setEndX(x);
setCrosshair = true;
} else if (x <= min){
LV1.setStartX(min); LV1.setEndX(min);
} else if (x >= max){
LV1.setStartX(max); LV1.setEndX(max);
}
if (setCrosshair) {
chart.setCursor(Cursor.CROSSHAIR);
} else {
chart.setCursor(Cursor.DEFAULT);
}
}
private static double getSceneShift(Node node) {
double shift = 0;
do {
shift += node.getLayoutX();
node = node.getParent();
} while (node != null);
return shift;
}
public static void main(String[] args) {
launch(args);
}
}
My code below plot 2 XY line charts in a VERTICAL divided SplitPane.
My question:
I would like to have both Y axis and only one X axis at the bottom (X axis on the lower graph only)
public class XyChartInSplit extends Application {
SplitPane splitPane1 = null;
#Override
public void start(Stage stage) {
stage.setTitle("Line plot");
final CategoryAxis xAxis1 = new CategoryAxis();
final NumberAxis yAxis1 = new NumberAxis(1, 21,0.1);
yAxis1.setTickUnit(1);
yAxis1.setPrefWidth(35);
yAxis1.setMinorTickCount(10);
yAxis1.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis1){
#Override
public String toString(Number object){
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final LineChart<String, Number>lineChart1 = new LineChart<String, Number>(xAxis1, yAxis1);
lineChart1.setCreateSymbols(false);
lineChart1.setAlternativeRowFillVisible(false);
lineChart1.setLegendVisible(false);
lineChart1.setAnimated(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 2));
series1.getData().add(new XYChart.Data("Mar", 1.5));
series1.getData().add(new XYChart.Data("Apr", 3));
series1.getData().add(new XYChart.Data("May", 2.5));
series1.getData().add(new XYChart.Data("Jun", 5));
series1.getData().add(new XYChart.Data("Jul", 4));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 6.5));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
//final BorderPane pane1 = new BorderPane();
//pane1.setCenter(lineChart1);
lineChart1.getData().addAll(series1);
final CategoryAxis xAxis2 = new CategoryAxis();
final NumberAxis yAxis2 = new NumberAxis(1, 21,0.1);
yAxis2.setTickUnit(1);
yAxis2.setPrefWidth(35);
yAxis2.setMinorTickCount(10);
yAxis2.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis2){
#Override
public String toString(Number object){
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final LineChart<String, Number>lineChart2 = new LineChart<String, Number>(xAxis2, yAxis2);
lineChart2.setCreateSymbols(false);
lineChart2.setAlternativeRowFillVisible(false);
lineChart2.setLegendVisible(false);
lineChart2.setAnimated(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data("Jan", 1));
series2.getData().add(new XYChart.Data("Feb", 3));
series2.getData().add(new XYChart.Data("Mar", 1.5));
series2.getData().add(new XYChart.Data("Apr", 3));
series2.getData().add(new XYChart.Data("May", 4.5));
series2.getData().add(new XYChart.Data("Jun", 5));
series2.getData().add(new XYChart.Data("Jul", 4));
series2.getData().add(new XYChart.Data("Aug", 8));
series2.getData().add(new XYChart.Data("Sep", 16.5));
series2.getData().add(new XYChart.Data("Oct", 13.9));
series2.getData().add(new XYChart.Data("Nov", 17));
series2.getData().add(new XYChart.Data("Dec", 20));
// final BorderPane pane2 = new BorderPane();
//pane2.setCenter(lineChart2);
lineChart2.getData().addAll(series2);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(lineChart1);
splitPane1.setDividerPosition(0, 1);
Scene scene = new Scene(splitPane1, 800, 600);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
splitPane1.getItems().addAll(lineChart2);
ObservableList<SplitPane.Divider> splitDiv = splitPane1.getDividers();
percSplit = 1/(double)(splitDiv.size()+1);
for (int i = 0; i< splitDiv.size(); i++) {
splitPane1.setDividerPosition(i, percSplit);
percSplit += 1/(double)(splitDiv.size()+1);
}
}
});
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You may want to vote for this feature request: https://javafx-jira.kenai.com/browse/RT-14710