I have problem with updating JavaFX UI - I want to update line chart and some labels on my scene when it's already shown.
My task is to do some calculations (calling function in other class which returns dataseries) and add updated series to the chart.
The following code (which is in a loop) may present what i want to do:
//double x - x value of the point i want to add to my chart
//double y - y value of the point i want to add to my chart
//string s - some result from function
mySeries.getData().add(new XYChart.Data(x, y));
someLabel.setText(s);
My program freezes and give only final solution after some time, but i want to see the points on the chart exactly after they're added, not at the end of the execution. If the process is too quick, i would like to add Thread.sleep(1000) before adding the next point to the chart.
I know it has something to do with threads, concurrency and tasks, but i wasn't able to find a solution yet. I tried to use some code I found here but still I don't know the correct answer.
Every user action, e.g. click a button, will notify your action listener within the UI thread. Logic in the UI thread should be as fast as possible. I think you are reacting on a user event and then execute a long running task in the UI thread. Try to put your code in a background thread. Further you need to put the UI updates back again in the UI thread. You can do this with "Platform.runLater(...)".
Something like this:
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createChart());
primaryStage.setScene(scene);
primaryStage.setHeight(800);
primaryStage.setWidth(1200);
primaryStage.show();
}
private Parent createChart() {
LineChart<Number, Number> lc = new LineChart<>(new NumberAxis(), new NumberAxis());
XYChart.Series<Number, Number> series = new XYChart.Series<>();
lc.getData().add(series);
new Thread(() -> {
try {
Thread.sleep(5000);
for (int i = 0; i < 15; i++) {
int finalI = i;
Platform.runLater(() -> series.getData().add(new XYChart.Data<>(1 + finalI, 1 + finalI)));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
return lc;
}
}
Related
I will try to be simple.
In the beginning of the main I create a new Stage which displays 2 button.
Depending on the button clicked, it will set a number to 1 or 2 in the Controller of the stage.
Then I create a condition, if the number is 1 I execute a certain part of the main.
If the number is 2, I execute the other part of the main.
The thing is that I have to wait for the response of the user.I did a while condition(while the number is 0 I come back in the while) but the programs doesn't work.
Do you have an idea to wait for the user's response ?
Here is the beginning of the code.
public void start(Stage primaryStage) {
try {
FXMLLoader loader1 = new FXMLLoader(getClass().getResource("SampleStart.fxml"));
Parent root1 = loader1.load();
SampleControllerStart StartController = (SampleControllerStart) loader1.getController();
Scene scene1 = new Scene(root1,474,435);
primaryStage.setScene(scene1);
primaryStage.show();
StartController.setEtat(0);
while(StartController.getEtat() == 0) {
System.out.println("ok");
}
if(StartController.getEtat() == 1) { ```
It seems what you're trying to do is have a dialog with a choice that will condition the rest of your program.
JavaFX is a bit overkill to achieve that, but you could use javafx.scene.control.Dialog and get what you're looking for.
Sample:
public void start(Stage stage) throws IOException {
ButtonType b1 = new ButtonType("Choice 1", ButtonData.LEFT);
ButtonType b2 = new ButtonType("Choice 2", ButtonData.RIGHT);
Dialog<ButtonType> diag = new Dialog<>();
diag.setTitle("My program"); //Not required
DialogPane pane = diag.getDialogPane();
pane.setHeaderText("You need to make a choice"); //Not required
pane.setContentText("Choose 1 or 2");
Circle icon = new Circle(20); //should be an image
icon.setFill(Paint.valueOf("blue"));
pane.setGraphic(icon); //Not required
pane.getButtonTypes().addAll(b1, b2);
Optional<ButtonType> res = diag.showAndWait();
if (!res.isPresent())
return;
System.out.println(res.get().getText() + " / " + res.get().getButtonData());
if (res.get().equals(b1)) {
System.out.println("Button 1 clicked");
//Do something
} else {
System.out.println("Button 2 clicked");
//Do something else
}
}
I'm using Dialog.showAndWait here to halt until user makes a choice. Never do a loop like you did without at least a Thread.sleep() inside it: this will max out your CPU for nothing, possibly rendering your program not responsive.
Preferably, you would have a full UI made with JavaFX, and handle the user's choices in event handlers for your own controls, and then do your actions in a javafx.concurrent.Service that would run it in the background, letting your UI responsive for status display or cancelling your task.
Or you don't really need a UI and use the console to take input from the user...
I want to do different manipulations on Label inside Table in libGDX.
in my Table class I create a Label and scale\move\rotate it, I'm trying to call to this actions every time the actions is finishes so each time It will run on different string, but I got a crash because of calling to my self every time (i think).
a= new Label("", mLabelStyleAmount);
addActor(a); // in a Table class
action = Actions.run(new Runnable() {
#Override
public void run() {
lunchText(i);
}
});
private void lunchText (int i) {
a.setText(strings[i]);
ScaleToAction up = Actions.scaleBy(30, 20, 5)
a.setPosition(a.getWidth() * 0.2f, 50);
a.addAction(Actions.sequence(up, action));
i++;
}
Is this is the right way to call the same method each time I finished?
The exception you've got is being caused by infinite recurrence that being caused by adding action in method that is being fired by action :) It means that every time you add the action to your action you add action again and so on and so on...
Second thing is that when you are adding many action by using addAction method these actions are parallel not sequential!
The resolution is to use libGDX SequenceAction and to generate all action at the beginning. It should like:
SequenceAction sequence = Actions.sequence();
for(int i = 0; i < string.length; i++) {
sequence.addAction(Actions.run(new Runnable() {
#Override
public void run() {
a.setText(strings[i]);
a.setPosition(a.getWidth() * 0.2f, 50);
}
}, Actions.scaleBy(30, 20, 5)));
Another way is to check in render() method if a do not have any actions (what means that last actions is finished) and then add new action
//render()
if(a.getActions.size == 0)
addActionToA();
I have a javafx Progressbar in my UI and then I have a for loop.
In every iteration of the for loop this happens:
progressVal += 5.0;
progress.setProgress(progressVal);
And after the whole loop, this happens:
progress.setProgress(100);
I now have recognized that the UI controls are only refreshed after the method has finished, so the progress bar wont update until the end and than would be set to 100 imidiatly.
So is it possible so somehow force the UI update, even when the method isn`t finished completly? Or how else could I do this?
If what you do in your loop takes a long time and should not be executed on the JavaFX application thread, (which it probably does or you probably wouldn't have this question), then you should probably run the loop in a Task, on a different thread, updating the task's progress as the loop progresses and binding the progress bar's value to the task's progress.
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ProgressFeedback extends Application {
private static final double EPSILON = 0.0000005;
#Override
public void start(Stage stage) throws Exception {
final Task<Void> task = new Task<Void>() {
final int N_ITERATIONS = 100;
#Override
protected Void call() throws Exception {
for (int i = 0; i < N_ITERATIONS; i++) {
updateProgress(i + 1, N_ITERATIONS);
// sleep is used to simulate doing some work which takes some time....
Thread.sleep(10);
}
return null;
}
};
final ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(
task.progressProperty()
);
// color the bar green when the work is complete.
progress.progressProperty().addListener(observable -> {
if (progress.getProgress() >= 1 - EPSILON) {
progress.setStyle("-fx-accent: forestgreen;");
}
});
// layout the app
final StackPane layout = new StackPane(progress);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
final Thread thread = new Thread(task, "task-thread");
thread.setDaemon(true);
thread.start();
}
public static void main(String[] args) {
launch(args);
}
}
what if the task makes call to a method that does only a single job , and do not want it to be called again and again?
Then remove the loop and invoke updateProgress through multiple calls within the job as the job progresses. The loop is only present in this sample for demonstration purposes and because this is what the original question specifically asked about.
For example lets say your job has 3 stages: fetch from database, perform calculation and accumulate results. Each stage taking an estimated 30 percent, 60 percent and 10 percent of total time respectively, then you can do something like this within the call() body of your task:
updateProgress(0, 1.0);
Data data = db.fetchData(query);
updateProgress(0.3, 1.0);
ProcessedData processed = calculator.process(data);
updateProgress(0.9, 1.0);
Result result = accumulator.reduce(processed);
updateProgress(1.0, 1.0);
Quotation from Java Doc:
The actual progress of the ProgressIndicator. A negative value for progress indicates that the progress is indeterminate. A positive value between 0 and 1 indicates the percentage of progress where 0 is 0% and 1 is 100%. Any value greater than 1 is interpreted as 100%.
That means in simple words, you must divide you value with 100, or you just increment the value by 0.05 instead of 0.5.
Happy Coding,
Kalasch
My friend show me example of using that. You could do this:
Platform.runLater(new Runnable() {
#Override public void run() {
bar.setProgress(counter/1000000.0);
}
});
And the refresh progress bar is in other Thread
I create some node in javaFX. And I need to animate each node to translate between some point to some point in Pane with TranslateTransition. But I need to make each of that node move independently using different starting point. How to make this happen? I mean is there any something like Thread.pause in javaFX. I tried using the rough approach like in the code below:
public void mouseRespon(final double x, final double y){
animasi.pause();
thread = new Thread(
new Runnable() {
#Override
public void run() {
int i = 0;
while(threadStatus && i < allList.size()){
final int j = i;
Platform.runLater(new Runnable() {
#Override
public void run() {
DynamicsText text = allList.get(j);
TranslateTransition transition = new TranslateTransition();
transition.setNode(text);
transition.setFromX(text.getTranslateX());
transition.setFromY(text.getTranslateY());
text.setDestinyOffset(x, y);
text.initCurrentOffSet();
double destX = text.getPotitionX();
double destY = text.getPotitonY();
transition.setToX(destX);
transition.setToY(destY);
transition.setDuration(Duration.seconds(1));
transition.setAutoReverse(false);
transition.setCycleCount(1);
transition.play();
}
});
try{
Thread.sleep(25);
}catch(Exception e){}
i++;
}
}
});
thread.start();
animasi.play();
}
But I think that is not a good approach. And actually, when I run that snippet make a program crash (sometime) .
Create a ParallelTransition.
For each piece that you want to move, create a SequentialTransition, a PauseTransition, and the TranslateTransition. The PauseTransitions should be incrementally bigger for each piece, say 0, 25ms, 50ms, 75ms, etc. Add the PauseTransition and the TranslateTransition to the SequentialTransition. Add each SequentialTransition to the ParallelTransition.
Then play the ParallelTransition.
No need to create any Threads or mess with Platform.runLater(...).
in c++ or c programming language, we know to change the cordinate we use gotoxy(x,y) and we can use looping and sleep to change the cordinate and making animation. like this;
for(x = 20; x < 25; x++){
gotoxy(x,20); cout << "*"
}
but my queston is how about in JAVAFX 2.0 programming? i'm using netbeans 7.2.
thanks for your any help.
Use the JavaFX Animation Package.
There are numerous examples in the JavaFX Animation Tutorial, as Andy pointed out in his comment.
And there is a cute example of a running horse animation loop.
The key is that you don't sleep the JavaFX application thread and you have to release control of the JavaFX thread back to the JavaFX system each time you update something and want it rendered. The JavaFX animation classes take care of these things for you so that you don't have to worry about it. If you just loop like you do in the sample code from your question, JavaFX will just render the scene once after your loop has completed and you will never see anything happen.
Here is a fairly boring example which uses a Timeline to emulate the c++ code in your question to move a dot a pixel every 400 milliseconds.
import java.util.Date;
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
/** Simple JavaFX Animation Sample. */
public class AnimationSample extends Application {
private int x = 20;
private String status = "";
private final Circle dot = new Circle(20, 20, 3);
private final TimeCounter counter = new TimeCounter();
public static void main(String[] args) throws Exception { launch(args); }
#Override public void start(final Stage stage) throws Exception {
final Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, new EventHandler() {
#Override public void handle(Event event) {
refreshScene();
}
}),
new KeyFrame(Duration.millis(400))
);
timeline.setCycleCount(Timeline.INDEFINITE);
stage.setScene(new Scene(new Group(dot), 50, 50));
stage.show();
counter.reset();
timeline.play();
}
private void refreshScene() {
gotoxy(x, 20);
status = "*****".equals(status) ? "*" : status + "*";
System.out.println(String.format("%7d", counter.elapsed()) + " ms " + x + " " + status);
if (x == 24) {
x = 20;
} else {
x++;
}
}
private void gotoxy(int x, int y) {
dot.setCenterX(x);
dot.setCenterY(y);
}
class TimeCounter {
private long start = new Date().getTime();
void reset() { start = new Date().getTime(); }
long elapsed() { return new Date().getTime() - start; }
}
}
There are three different options in JavaFX, depending on your needs.
The most basic one is AnimationTimer. It's equivalent to Swing's Timer. It simply contains a handle method which is called on every frame, and passed current time as argument. You probably want some internal bookkeeping so that you do not do expensive calculations every time handle is called.
Transition has an interpolate(frac) method, which gets called with values of frac between 0.0 and 1.0. It's up to you to do all UI changes you want to, based on the frac value. Both Transition and Timeline extend Animation, so you can set stuff like cycle duration, whether the Transition is reversed at end, etc.
Timeline is the most complex one. You define arbitrary amount of KeyFrames (think of states) that contain wanted properties of different Nodes, and the Timeline will do all the work for you interpolating how to animate the change between the provided values. For example, you can give a keyframe where x property of a Node is 0, and another where it's 100, and Timeline will do the animating for you.
Have look at using a Timeline Animation. It is a key component of animation in JavaFX and
is used to establish when, and in what sequence, key parts of an animation occur.
Here is an example