This code plots a simple XYLine Chart
import javafx.application.Application;
import javafx.event.EventHandler;
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.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class XyChart extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("Line plot");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21,0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
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 pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I would like to draw arrows on the chart by left mouse click pressed and moved, such as this example
How to do this?
Thanks all.
You can add mouse handlers to the border pane and draw the arrow when the mouse moves.
There might be better ways, but this is what I came up with:
Add mouse handler to your border pane:
MouseHandler mh = new MouseHandler( pane );
pane.setOnMouseClicked( mh );
pane.setOnMouseMoved( mh );
The handler class could be as follows:
class MouseHandler implements EventHandler< MouseEvent > {
private boolean gotFirst = false;
private Line line, head1, head2;
private Pane pane;
private double x1, y1, x2, y2;
double phi = Math.toRadians( 40 );
double barb = 20;
public MouseHandler( Pane pane ) {
this.pane = pane;
}
#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 );
head1 = new Line( x2, y2, x2, y2 );
head2 = new Line( x2, y2, x2, y2 );
pane.getChildren().add( line );
pane.getChildren().add( head1 );
pane.getChildren().add( head2 );
gotFirst = true;
} else {
line = null;
gotFirst = false;
}
} else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
// draw head
// http://www.coderanch.com/t/340443/GUI/java/Draw-arrow-head-end-line
double dx = x2 - x1;
double dy = y2 - y1;
double theta = Math.atan2( dy, dx );
double x, y, rho = theta + phi;
x = x2 - barb * Math.cos( rho );
y = y2 - barb * Math.sin( rho );
head1.setStartX( x2 );
head1.setStartY( y2 );
head1.setEndX( x );
head1.setEndY( y );
rho = theta - phi;
x = x2 - barb * Math.cos( rho );
y = y2 - barb * Math.sin( rho );
head2.setStartX( x2 );
head2.setStartY( y2 );
head2.setEndX( x );
head2.setEndY( y );
}
}
}
}
Related
This code plots a XY LineChart and (code thanks to Bhupendra) draw lines on chart by left mouse click and if mouse hover, line get selected turns to red and can be deleted or moved.
import javafx.application.Application;
import javafx.event.EventHandler;
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.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 );
}
}
}
}
}
What I would like to do now is to change line length or slope by selecting one of the end or tail points (A and B in the picture) and set new end x,y point
How to accomplish this?
Thanks.
Use the following class instead of Line ...
public class EditLine extends Parent {
private static final double radius = 5;
private Line line;
private Circle c1, c2;
private class MouseHandler implements EventHandler< MouseEvent > {
private boolean isfirst;
public MouseHandler( boolean first ) {
isfirst = first;
}
public void handle( MouseEvent event ) {
if( event.getEventType() == MouseEvent.MOUSE_ENTERED ) {
if( isfirst )
c1.setOpacity( 1 );
else c2.setOpacity( 1 );
} else if( event.getEventType() == MouseEvent.MOUSE_EXITED ) {
if( isfirst )
c1.setOpacity( 0 );
else c2.setOpacity( 0 );
} else if( event.getEventType() == MouseEvent.MOUSE_DRAGGED ) {
double x = event.getSceneX();
double y = event.getSceneY();
if( isfirst ) {
line.setStartX( x );
line.setStartY( y );
c1.setCenterX( x );
c1.setCenterY( y );
} else {
line.setEndX( x );
line.setEndY( y );
c2.setCenterX( x );
c2.setCenterY( y );
}
}
}
}
public EditLine( double x1, double y1, double x2, double y2 ) {
line = new Line( x1, y1, x2, y2 );
line.setMouseTransparent( true );
c1 = new Circle( x1, y1, radius, Color.RED );
c1.setOpacity( 0 );
MouseHandler mh1 = new MouseHandler( true );
c1.setOnMouseEntered( mh1 );
c1.setOnMouseExited( mh1 );
c1.setOnMouseDragged( mh1 );
c2 = new Circle( x2, y2, radius, Color.RED );
c2.setOpacity( 0 );
MouseHandler mh2 = new MouseHandler( false );
c2.setOnMouseEntered( mh2 );
c2.setOnMouseExited( mh2 );
c2.setOnMouseDragged( mh2 );
getChildren().addAll( c1, c2, line );
}
}
This code below draw lines on an XY lineChart
import javafx.application.Application;
import javafx.event.EventHandler;
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 Lines 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(0.5, 9.5, 0.1);
yAxis.setTickUnit(1);
//yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
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.5));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 6.5));
series1.getData().add(new XYChart.Data("May", 4.5));
series1.getData().add(new XYChart.Data("Jun", 8.5));
series1.getData().add(new XYChart.Data("Jul", 6.5));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
Lines.MouseHandler mh = new Lines.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;
public MouseHandler( Pane pane ) {
this.pane = pane;
}
#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 = null;
gotFirst = false;
}
}
else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
}
}
}
}
}
I would like to copy (clone) a line once drawn, such as this picture
So, first left mouse click on point 1, then second mouse click on point 2: my goal is to have now a copy of this line (same lenght, same slope) that I can place anywhere on the chart, in this example by a third left mouse click on point 3.
How to do this?
Thanks!
This code draw lines on a chart by left mouse click and move
import javafx.application.Application;
import javafx.event.EventHandler;
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 Lines 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.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);
Lines.MouseHandler mh = new Lines.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;
public MouseHandler( Pane pane ) {
this.pane = pane;
}
#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 = null;
gotFirst = false;
}
}
else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
}
}
}
}
}
My question is: how to edit such lines once plotted?
In example, once one or more line(s) are drawn I would like to select one of these, and by right mouse click and using a pop-up menu, delete it, modify length, (to make it shorter or longer) or change line slope.
Thanks all.
The following modifications will add functionality to move the line when dragged with left mouse button.
Also, the line will be removed when clicked with right mouse button.
This is the line handler callback class.
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();
}
}
Create the line handler in mouse handler class:
private LineHandler lineHandler;
public MouseHandler( Pane pane ) {
this.pane = pane;
lineHandler = new LineHandler(pane);
}
Add the handler to each line in the else clause of !gotFirst
} 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;
}
You can add the line remove functionality to a popup event.
I my code below I plot two series in a SplitPane, but I have some white borders I would like to remove, so that the grids will fill all the chart space.
Here is a picture to show where I would like to remove
and this is my code
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.Chart;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.SplitPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class XyChartInSplitMove extends Application {
SplitPane splitPane1 = null;
BorderPane pane;
BorderPane pane2;
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);
xAxis.setAnimated(false);
yAxis.setAnimated(false);
xAxis.setScaleX(0);
xAxis.setVisible(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);
pane = new BorderPane();
pane.setCenter(lineChart1);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(pane);
splitPane1.setDividerPosition(0, 1);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
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);
}
}
});
//BarChart
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> barChart2 = new BarChart<String, Number>(xAxis2, yAxis2);
barChart2.setAlternativeRowFillVisible(false);
barChart2.setLegendVisible(false);
barChart2.setAnimated(false);
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));
barChart2.getData().addAll(series2);
pane2 = new BorderPane();
pane2.setCenter(barChart2);
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);
pane.setOnMouseClicked(mouseHandler);
pane.setOnMouseDragged(mouseHandler);
pane.setOnMouseEntered(mouseHandler);
pane.setOnMouseExited(mouseHandler);
pane.setOnMouseMoved(mouseHandler);
pane.setOnMouseReleased(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
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 newXlower = xAxis.getLowerBound(), newXupper = xAxis.getUpperBound();
double newYlower = yAxis.getLowerBound(), newYupper = yAxis.getUpperBound();
double delta = 0.3;
if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
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);
}
rectinitX.set(mouseEvent.getX());
rectinitY.set(mouseEvent.getY());
BarChart<String, Number> barChart2 = (BarChart<String, Number>) pane2.getCenter();
double chartWidth = xAxis.getWidth();
double axisSpan = xAxis.getUpperBound() - xAxis.getLowerBound();
double displacement = (chartWidth / axisSpan) * (1 - newXlower);
for (Node node : barChart2.getChildrenUnmodifiable()) {
if (node.getClass().getEnclosingClass() == Chart.class) {
for (Node node2 : ((Parent) node).getChildrenUnmodifiable()) {
if ((node2 == barChart2.getXAxis())) {
node2.translateXProperty().set(displacement);
}
if (node2.getClass().getEnclosingClass() == XYChart.class) {
for (Node node3 : ((Parent) node2).getChildrenUnmodifiable()) {
if (node3.getClass() == Group.class) {
node3.translateXProperty().set(displacement);
}
}
}
}
}
}
}
}
};
public static void main(String[] args) {
launch(args);
}
}
To hide the upper X axis I have used xAxis.setScaleX(0); but I am not sure if this is the best way to hide X axis.
Any help really appreciated
Define a style.css:
.chart {
-fx-padding: 1px;
}
.chart-content {
-fx-padding: 0px;
}
Add this file to the stylesheets of app:
scene.getStylesheets().add(this.getClass().getResource("style.css").toExternalForm());
See the results.
Further. To hide the gap between top and bottom charts, you can move top chart to down like this, the gap appears on the top however:
...
...
stage.show();
// It should be after stage.show()
lineChart1.setTranslateY(xAxis.getHeight());
// Or give constant value if you want to put this code before the stage.show()
// lineChart1.setTranslateY(28);
My class below plots two charts in a splitpane.
By left mouse click and drag the upper line chart moves left/right (and up/down).
I would like to move both panes together left/right when I click and drag with left mouse on the upper graph: how to accomplish this?
Thanks
public class XyChartInSplitMove extends Application {
SplitPane splitPane1 = null;
BorderPane pane;
BorderPane pane2;
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);
xAxis.setAnimated(false);
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);
pane = new BorderPane();
pane.setCenter(lineChart1);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(pane);
splitPane1.setDividerPosition(0, 1);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
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);
}
}
});
//BarChart
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>BarChart2 = new BarChart<String, Number>(xAxis2, yAxis2);
BarChart2.setAlternativeRowFillVisible(false);
BarChart2.setLegendVisible(false);
BarChart2.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));
BarChart2.getData().addAll(series2);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
splitPane1.getItems().addAll(BarChart2);
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);
pane.setOnMouseClicked(mouseHandler);
pane.setOnMouseDragged(mouseHandler);
pane.setOnMouseEntered(mouseHandler);
pane.setOnMouseExited(mouseHandler);
pane.setOnMouseMoved(mouseHandler);
pane.setOnMouseReleased(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent mouseEvent) {
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 = getSceneShift(xAxis);
double yAxisShift = getSceneShift(yAxis);
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){
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);
}
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;
int XLB=(int) xAxis.getLowerBound();
int XUB=(int) xAxis.getUpperBound();
}
}
}
};
private static double getSceneShift(Node node) {
double shift = 0;
do {
shift += node.getLayoutX();
node = node.getParent();
} while (node != null);
return shift;
}
private static String getHIstLOstY(XYChart.Series S,int XLowerBound,int XUpperBound) {
double ValLOst=1000000;
double ValHIst=-1000000;
for(int i=XLowerBound; i<XUpperBound; i++){
double P=GetPrice(S,i);
if(ValHIst<P){
ValHIst=P;
}
if(ValLOst>P){
ValLOst=P;
}
}
return Double.toString(ValLOst) + "," + Double.toString(ValHIst);
}
private static double GetPrice(XYChart.Series S,int IX) {
Object SVal=S.getData().get(IX);
String Temp=SVal.toString().replaceAll("Data", "");
Temp=Temp.replace("[", "");
Temp=Temp.replace("]", "");
String[] TempArray=Temp.split(",");
return Double.parseDouble(TempArray[1]);
}
public static void main(String[] args) {
launch(args);
}
}
Option 1 (simple):
Imagine you have the same mouse handlers in both charts, then you can drag both charts separately. In this circumstances you need to update mouse handlers in top chart to call same events in bottom chart (minus y coordinate difference)
Option 2 (good):
Bind bottom chart axis ranges to top chart ones (yes, it may involve some complex math) as you did it for chart's starting states. So on update of top chart bottom chart will get updated automatically.