So I have a javafx line chart in my program. The program generates a random value from 1-6 every second and plots it on the graph. So the X axis represents the time in seconds and the Y axis represents the random value chosen. How would I go about moving the graph on the x axis as time goes by? i.e animating the graph going in the right direction.
public class MainDisplayController implements Initializable {
LineChart<String,Number> lineChart;
Timer timer = new Timer();
TimerTask task;
Random dice = new Random();
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
XYChart.Series<String,Number> series = new XYChart.Series<String,Number>();
series.getData().add(new XYChart.Data<String,Number>("1",0));
lineChart.getData().add(series);
task = new TimerTask(){
int secondsPassed = 0;
#Override
public void run() {
secondsPassed++;
System.out.println(secondsPassed);
int number;
number = 1+dice.nextInt(6);
series.getData().add(new XYChart.Data<String,Number>(String.valueOf(secondsPassed),number));
}
};
timer.scheduleAtFixedRate(task, 1000, 1000);
}
}
I think you should try setting:
xAxis.setForceZeroInRange(false);
And then remove data indexed 0 (first one) from your series when certain condition is met.
Here is some code that does something similar, I hope it helps.
public class PointEverySecondLineChart extends Application {
private BorderPane root = new BorderPane();
private Scene scene = new Scene(root,800,600);
private Random rand = new Random();
private SimpleIntegerProperty
lastX = new SimpleIntegerProperty(0);
private XYChart.Series <Integer,Integer> mySeries;
private NumberAxis
xAxis = new NumberAxis(),
yAxis = new NumberAxis();
private LineChart lineChart = new LineChart<>(xAxis,yAxis);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Timeline addPointEverySecond = new Timeline(new KeyFrame(Duration.millis(500),event->{
lastX.set(lastX.add(1).get());
mySeries.getData().add(new XYChart.Data<>(lastX.get(), rand.nextInt(100)));
if (mySeries.getData().size()>10) mySeries.getData().remove(0);
}));
addPointEverySecond.setCycleCount(Timeline.INDEFINITE);
ObservableList <XYChart.Series<Integer,Integer>> data = FXCollections.observableArrayList();
LineChart<Integer,Integer> chart = makeLineChart(data);
chart.setPrefWidth(600);
chart.setPrefHeight(600);
root.setCenter(chart);
Button btGo = new Button("GO!");
btGo.setOnAction(action -> {
mySeries = new XYChart.Series<>();
data.add(mySeries);
lastX.set(0);
addPointEverySecond.playFromStart();
});
btGo.disableProperty().bind(addPointEverySecond.statusProperty().isEqualTo(Animation.Status.RUNNING));
root.setBottom(btGo);
primaryStage.setScene(scene);
primaryStage.show();
}
LineChart<Integer, Integer> makeLineChart(ObservableList <XYChart.Series<Integer,Integer>> series) {
xAxis.setForceZeroInRange(false);
lineChart.setCreateSymbols(false);
lineChart.setData(series);
return lineChart;
}
}
Related
I'm trying to make bar chart by Javafx. However It quite small to see it.
I want to make it more attractive like
Here is code's program
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("JavaFX Chart Demo");
StackPane pane = new StackPane();
pane.getChildren().add(createBarChart());
stage.setScene(new Scene(pane, 400, 200));
stage.show();
}
public ObservableList<XYChart.Series<String, Double>>
getDummyChartData() {
ObservableList<XYChart.Series<String, Double>> data =
FXCollections.observableArrayList();
Series<String, Double> as = new Series<>();
Series<String, Double> bs = new Series<>();
Series<String, Double> cs = new Series<>();
Series<String, Double> ds = new Series<>();
Series<String, Double> es = new Series<>();
Series<String, Double> fs = new Series<>();
as.setName("A-Series");
bs.setName("B-Series");
cs.setName("C-Series");
ds.setName("D-Series");
es.setName("E-Series");
fs.setName("F-Series");
Random r = new Random();
for (int i = 1900; i < 2017; i += 10) {
as.getData().add(new XYChart.Data<>
(Integer.toString(i), r.nextDouble()));
bs.getData().add(new XYChart.Data<>
(Integer.toString(i), r.nextDouble()));
cs.getData().add(new XYChart.Data<>
(Integer.toString(i), r.nextDouble()));
ds.getData().add(new XYChart.Data<>
(Integer.toString(i), r.nextDouble()));
es.getData().add(new XYChart.Data<>
(Integer.toString(i), r.nextDouble()));
fs.getData().add(new XYChart.Data<>
(Integer.toString(i), r.nextDouble()));
}
data.addAll(as, bs, cs, ds, es, fs);
return data;
}
public XYChart<CategoryAxis, NumberAxis>
createBarChart() {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis();
BarChart bc = new BarChart<>(xAxis, yAxis);
bc.setData(getDummyChartData());
bc.setTitle("Bar Chart on Random Number");
return bc;
}
}
Please help me how to get it by Javafx. I found it can be solve by JFree Chart However.I don't know how to make it by BarChart javafX. It's really challenge to me these day. Thank you
Just make the chart in a scroll pane.
public void start(Stage stage) throws Exception {
stage.setTitle("JavaFX Chart Demo");
StackPane stackPane = new StackPane();
stackPane.setPrefSize(1000, 200);
stackPane.getChildren().add(createBarChart());
ScrollPane scrollPane = new ScrollPane();
scrollPane.setPrefSize(400, 220);
scrollPane.setContent(stackPane);
stage.setScene(new Scene(scrollPane));
stage.show();
}
i'm looking for a system to render this animation of the line dynamic with variable change:
public class FXMLDocumentController implements Initializable {
private long batt = 0;
#FXML
private Line lancettaBatteria;
public long mappa(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
#Override
public void initialize(URL url, ResourceBundle rb) {
Random random = new Random();
batt = random.nextInt(100);
long valMappatoBatteria = this.mappa(batt, 0, 100, -40, 135);
Rotate rotazioneBatteria = new Rotate();
lancettaBatteria.getTransforms().add(rotazioneBatteria);
Timeline timelineBatteria = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rotazioneBatteria.angleProperty(), -40)),
new KeyFrame(Duration.seconds(3), new KeyValue(rotazioneBatteria.angleProperty(), valMappatoBatteria)));
timelineBatteria.play();
}
with this code it show only the first random number, my target is to move the line for infinite time with the relative random number generated(i need the random number for display the line in particular position), is this possible? i try to sorround all with a while(true)
public void initialize(URL url, ResourceBundle rb) {
while(true){
Random random = new Random();
batt = random.nextInt(100);
long valMappatoBatteria = this.mappa(batt, 0, 100, -40, 135);
Rotate rotazioneBatteria = new Rotate();
lancettaBatteria.getTransforms().add(rotazioneBatteria);
Timeline timelineBatteria = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rotazioneBatteria.angleProperty(), -40)),
new KeyFrame(Duration.seconds(3), new KeyValue(rotazioneBatteria.angleProperty(), valMappatoBatteria)));
timelineBatteria.play();
}
}
but the app stop to work.
General Approaches
In general, an infinite animation in Java can be achieved multiple ways.
Here are a few:
Set the cycle count to indefinite to have an animation play forever:
Timeline.setCycleCount(Timeline.INDEFINITE);
Also setAutoReverse to true if you want it to go back and forth.
Use an AnimationTimer.
Use a Timeline and add an onFinished handler to the timeline which updates some relevant Keyframes within the timeline as necessary and then plays the timeline from start again.
The third approach of using an onFinishedHandler is the approach followed in the specific example below.
Specific Example
This example is based upon the requirements of your question as I understand them. I have no idea what why you are trying to do this. But as far as I understand what you are trying to do, the following app will do it.
Start position of each rotation:
Random max position of a rotation:
What it does is create a timeline which will update a value in a rotation transform for a line continuously. The line starts from a starting rotation angle, animates to a random maximum value and then animates back. Once the line reaches its starting position, a new random maximum value for the rotation is generated and the line animates to this new maximum value and back again. The process continues indefinitely. The setOnFinishedHandler of the timeline animation is the point which calculates the new random maximum value and updates the keyframe for the maximum animation value appropriately.
So that may or may not be exactly what you are trying to do, but perhaps it is enough for you to implement what you need.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
public class VariableLine extends Application {
private static final double S = 100;
#Override
public void start(Stage stage) throws Exception {
RandomRotator randomRotator = new RandomRotator();
Line line = new Line(0, S, S, S);
randomRotator.getRotate().setPivotY(S);
line.getTransforms().add(randomRotator.getRotate());
Label maxValueText = new Label(randomRotator.getMaxAngle() + "");
maxValueText.textProperty().bind(randomRotator.maxAngleProperty().asString());
stage.setScene(new Scene(new Pane(maxValueText, line), S, S * 2));
stage.show();
randomRotator.getTimeline().play();
}
public static void main(String[] args) {
launch(args);
}
}
class RandomRotator {
private static final Random random = new Random(42);
private static final double INIT_ANGLE = -40;
private static final double MAX_ANGLE = 90;
private static final Duration ROTATION_DURATION = Duration.seconds(3);
private final ReadOnlyDoubleWrapper maxAngle = new ReadOnlyDoubleWrapper(INIT_ANGLE);
private final Timeline timeline = new Timeline();
private final Rotate rotate = new Rotate(INIT_ANGLE);
RandomRotator() {
timeline.getKeyFrames().addAll(
new KeyFrame(
Duration.seconds(0),
new KeyValue(rotate.angleProperty(), INIT_ANGLE)
),
new KeyFrame(
ROTATION_DURATION.divide(2),
new KeyValue(rotate.angleProperty(), maxAngle.get())
),
new KeyFrame(
ROTATION_DURATION,
new KeyValue(rotate.angleProperty(), INIT_ANGLE)
)
);
timeline.setOnFinished(event -> {
maxAngle.set(random.nextInt((int) MAX_ANGLE));
timeline.getKeyFrames().set(
1,
new KeyFrame(
ROTATION_DURATION.divide(2),
new KeyValue(rotate.angleProperty(), maxAngle.get())
)
);
timeline.playFromStart();
});
}
Rotate getRotate() {
return rotate;
}
public double getMaxAngle() {
return maxAngle.get();
}
public ReadOnlyDoubleProperty maxAngleProperty() {
return maxAngle.getReadOnlyProperty();
}
public Timeline getTimeline() {
return timeline;
}
}
I have javafx application that show some info.
I am new to javafx and trying to understand things while trying some tests.
I want to add clock on top of the application, and i found next source
How can i add this clock (from the source) to my current application on same screen (on top of the screen)? my app use next code on main.java (i am using also FXML file and edit it by Scene builder):
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import net.sourceforge.zmanim.hebrewcalendar.JewishCalendar;
import net.sourceforge.zmanim.hebrewcalendar.HebrewDateFormatter;
import net.sourceforge.zmanim.hebrewcalendar.JewishCalendar;
import net.sourceforge.zmanim.util.GeoLocation;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
Label lblShabat = (Label) root.lookup("#shabat");
Label lbldateHeb = (Label) root.lookup("#dateHeb");
JewishCalendar israelCalendar = new JewishCalendar();
israelCalendar.setInIsrael(true); //set the calendar to Israel
JewishCalendar chutsLaaretzCalendar = new JewishCalendar();
chutsLaaretzCalendar.setInIsrael(false); //not really needed since the API defaults to false
JewishCalendar jd = new JewishCalendar();
HebrewDateFormatter hdf = new HebrewDateFormatter();
hdf.setHebrewFormat(true);
for(int i = 0; i < 14; i++){
israelCalendar.forward(); //roll the date forward a day
// chutsLaaretzCalendar.forward(); //roll the date forward a day
if(israelCalendar.getDayOfWeek() == 7){ //ignore weekdays
if (lblShabat!=null) lblShabat.setText(hdf.formatYomTov(jd)); //hdf.formatParsha(israelCalendar)
//hdf.formatYomTov(jd)
}
}
String cholHamoedSuccos = "חול המועד סוכות";
if(hdf.formatYomTov(jd) == cholHamoedSuccos) {
String image = Main.class.getResource("Dollarphotoclub_91486993.jpg").toExternalForm();
root.setStyle("-fx-background-image: url('" + image + "'); " +
"-fx-background-position: center center; " +
"-fx-background-repeat: stretch;");
}
if (lbldateHeb!=null) lbldateHeb.setText(hdf.format(jd));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.setFullScreen(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
There is perhaps a better way that doesn't require as much refactoring, but I refactored Clock so a function createLayout() is separated from the start() function of Clock. In SceneBuilder add a subscene where you want the clock placed and set an id for it, I used clocksubscene. In the controller initialize() function create the layout and set the root of clocksubscene to layout. Also added the clock.css to the styles of main scene.
Refactored Clock.java (ommitted parts were unchanged) :
public void start(final Stage stage) throws Exception {
Parent layout = createLayout();
final Scene scene = new Scene(layout, Color.TRANSPARENT);
scene.getStylesheets().add(getResource("clock.css"));
// show the scene.
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
public Parent createLayout() {
// construct the analogueClock pieces.
final Circle face = new Circle(100, 100, 100);
face.setId("face");
final Label brand = new Label("Splotch");
brand.setId("brand");
brand.layoutXProperty().bind(face.centerXProperty().subtract(brand.widthProperty().divide(2)));
brand.layoutYProperty().bind(face.centerYProperty().add(face.radiusProperty().divide(2)));
final Line hourHand = new Line(0, 0, 0, -50);
hourHand.setTranslateX(100);
hourHand.setTranslateY(100);
hourHand.setId("hourHand");
final Line minuteHand = new Line(0, 0, 0, -75);
minuteHand.setTranslateX(100);
minuteHand.setTranslateY(100);
minuteHand.setId("minuteHand");
final Line secondHand = new Line(0, 15, 0, -88);
secondHand.setTranslateX(100);
secondHand.setTranslateY(100);
secondHand.setId("secondHand");
final Circle spindle = new Circle(100, 100, 5);
spindle.setId("spindle");
Group ticks = new Group();
for (int i = 0; i < 12; i++) {
Line tick = new Line(0, -83, 0, -93);
tick.setTranslateX(100);
tick.setTranslateY(100);
tick.getStyleClass().add("tick");
tick.getTransforms().add(new Rotate(i * (360 / 12)));
ticks.getChildren().add(tick);
}
final Group analogueClock = new Group(face, brand, ticks, spindle, hourHand, minuteHand, secondHand);
// construct the digitalClock pieces.
final Label digitalClock = new Label();
digitalClock.setId("digitalClock");
// determine the starting time.
Calendar calendar = GregorianCalendar.getInstance();
final double seedSecondDegrees = calendar.get(Calendar.SECOND) * (360 / 60);
final double seedMinuteDegrees = (calendar.get(Calendar.MINUTE) + seedSecondDegrees / 360.0) * (360 / 60);
final double seedHourDegrees = (calendar.get(Calendar.HOUR) + seedMinuteDegrees / 360.0) * (360 / 12);
// define rotations to map the analogueClock to the current time.
final Rotate hourRotate = new Rotate(seedHourDegrees);
final Rotate minuteRotate = new Rotate(seedMinuteDegrees);
final Rotate secondRotate = new Rotate(seedSecondDegrees);
hourHand.getTransforms().add(hourRotate);
minuteHand.getTransforms().add(minuteRotate);
secondHand.getTransforms().add(secondRotate);
// the hour hand rotates twice a day.
final Timeline hourTime = new Timeline(
new KeyFrame(
Duration.hours(12),
new KeyValue(
hourRotate.angleProperty(),
360 + seedHourDegrees,
Interpolator.LINEAR
)
)
);
// the minute hand rotates once an hour.
final Timeline minuteTime = new Timeline(
new KeyFrame(
Duration.minutes(60),
new KeyValue(
minuteRotate.angleProperty(),
360 + seedMinuteDegrees,
Interpolator.LINEAR
)
)
);
// move second hand rotates once a minute.
final Timeline secondTime = new Timeline(
new KeyFrame(
Duration.seconds(60),
new KeyValue(
secondRotate.angleProperty(),
360 + seedSecondDegrees,
Interpolator.LINEAR
)
)
);
// the digital clock updates once a second.
final Timeline digitalTime = new Timeline(
new KeyFrame(Duration.seconds(0),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
Calendar calendar = GregorianCalendar.getInstance();
String hourString = pad(2, '0', calendar.get(Calendar.HOUR) == 0 ? "12" : calendar.get(Calendar.HOUR) + "");
String minuteString = pad(2, '0', calendar.get(Calendar.MINUTE) + "");
String secondString = pad(2, '0', calendar.get(Calendar.SECOND) + "");
String ampmString = calendar.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM";
digitalClock.setText(hourString + ":" + minuteString + ":" + secondString + " " + ampmString);
}
}
),
new KeyFrame(Duration.seconds(1))
);
// time never ends.
hourTime.setCycleCount(Animation.INDEFINITE);
minuteTime.setCycleCount(Animation.INDEFINITE);
secondTime.setCycleCount(Animation.INDEFINITE);
digitalTime.setCycleCount(Animation.INDEFINITE);
// start the analogueClock.
digitalTime.play();
secondTime.play();
minuteTime.play();
hourTime.play();
// add a glow effect whenever the mouse is positioned over the clock.
final Glow glow = new Glow();
analogueClock.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
analogueClock.setEffect(glow);
}
});
analogueClock.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
analogueClock.setEffect(null);
}
});
// layout the scene.
final VBox layout = new VBox();
layout.getChildren().addAll(analogueClock, digitalClock);
layout.setAlignment(Pos.CENTER);
return layout;
}
new Controller initialize code:
#Override
public void initialize(URL url, ResourceBundle rb) {
Clock clock = new Clock();
clocksubscene.setRoot(clock.createLayout());
}
In main start routine:
Scene scene = new Scene(root, 300, 275);
scene.getStylesheets().add(Clock.getResource("clock.css"));
primaryStage.setScene(scene);
TheStageOfthesource.getScene().getRoot();//will give you the root pane
is that what you want ? then you add it to your Pane
I am working on a project and I need you help.
I want to know that is this possible to set anchorpane constraints in percentage as something like
AnchorPane.setLeftAnchor(content1, 35%);
Yes it can be by updating constraint values on every scene size change:
public class AnchorDemo extends Application {
private final Button button = new Button("Add");
private final ListView list = new ListView();
#Override
public void start(Stage primaryStage) {
AnchorPane root = new AnchorPane();
AnchorPane.setTopAnchor(list, 10.0);
AnchorPane.setLeftAnchor(list, 10.0);
AnchorPane.setTopAnchor(button, 10.0);
AnchorPane.setRightAnchor(button, 10.0);
root.getChildren().addAll(list, button);
Scene scene = new Scene(root, 300, 250);
scene.widthProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
updateWidthConstaints(newValue.doubleValue());
}
});
primaryStage.setScene(scene);
primaryStage.show();
updateWidthConstaints(scene.getWidth());
}
private void updateWidthConstaints(double width) {
// roughly give to the list 66% while to the button 33% of available
// space, besides paddings.
// +5s are for extra padding
AnchorPane.setRightAnchor(list, width * 1 / 3 + 5);
AnchorPane.setLeftAnchor(button, width * 2 / 3 + 5);
}
public static void main(String[] args) {
launch(args);
}
}
Here is my code which generates bar chart of 10 values from 0 to 10 . i want to change the color of bars as follows
if i>5 color==red
if i>8 color==blue
so the final out will be 0-5(default yellow bars) 6-8(Red bars) 9(blue bar)
kindly help me..
thanks
public class BarChartSample extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("Bar Chart Sample");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final BarChart < String, Number > bc = new BarChart < String, Number > (xAxis, yAxis);
bc.setTitle("Country Summary");
xAxis.setLabel("bars");
yAxis.setLabel("Value");
XYChart.Series series1 = new XYChart.Series();
series1.setName("...");
for (int i = 0; i < 10; i++) {
//here i want to change color of bar if value of i is >5 than red if i>8 than blue
series1.getData().add(new XYChart.Data("Value", i));
}
}
public static void main(String[] args) {
launch(args);
}
}
I created a sample solution.
The solution works by setting the bar's -fx-bar-fill color to a different color based on the value of the bar's data.
final XYChart.Data<String, Number> data = new XYChart.Data("Value " + i , i);
data.nodeProperty().addListener(new ChangeListener<Node>() {
#Override public void changed(ObservableValue<? extends Node> ov, Node oldNode, Node newNode) {
if (newNode != null) {
if (data.getYValue().intValue() > 8 ) {
newNode.setStyle("-fx-bar-fill: navy;");
} else if (data.getYValue().intValue() > 5 ) {
newNode.setStyle("-fx-bar-fill: firebrick;");
}
}
}
});