how latch has no effect in javafx? - javafx

I encounter a problem in developing javafx, I find latch has no effect in JavaFx, for example, in the following code:
public class JavafxLatchDemo1 extends Application {
#Override
public void start(Stage primaryStage) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
TextArea txtOut = new TextArea();
StackPane root = new StackPane();
root.getChildren().add(txtOut);
Scene scene = new Scene(root, 300, 250);
//invoke rpc function
Callable<Integer> fibCall = new fibCallable(latch, txtOut);
FutureTask<Integer> fibTask = new FutureTask<Integer>(fibCall);
Thread fibThread = new Thread(fibTask);
fibThread.start();
latch.await(); //阻塞等待计数为0
txtOut.appendText("\n Say 'Hello World'");
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
class fibCallable implements Callable<Integer>{
CountDownLatch latch;
TextArea txtInput;
fibCallable(CountDownLatch mylatch, TextArea txtIn){
latch = mylatch;
txtInput = txtIn;
}
#Override
public Integer call() throws Exception {
int temp1=1,temp2=0;
System.out.println("Client will pay money for eshop");
for(int i=0; i<10; i++){
temp1 = temp1 + temp2;
temp2 = temp1;
}
System.out.println("Client already decide to pay money for eshop");
Platform.runLater(()->{
txtInput.appendText("\nWhy, am I first?");
});
latch.countDown(); //计数减1
return (new Integer(temp1));
}
}
Since I set a latch to stop JavaFx main thread in latch.await();, and want the callable thread fibCallable output the content first: so I expect:
Why, am I first?
Say 'Hello World'
but the real output is opposite:
Say 'Hello World'
Why, am I first?
why? and a solution?

Platform.runLater() submits a runnable to be executed on the FX Application Thread. The start() method is also executed on the FX Application Thread. So the Runnable you submitted with Platform.runLater() cannot be executed until anything already executing on that thread completes.
So you start your fibThread in the background and then immediately wait for the latch: this blocks the FX Application Thread. The fibThread does a little bit of work and then submits a call to the (blocked) FX Application Thread. Then the fibThread releases the latch: the currently-blocked FX Application thread unblocks and finishes the current method call (appending the text "Say Hello World" to the text area and displaying the stage), and at some point after that the runnable submitted to Platform.runLater() executes on the same thread.
A "quick and dirty" fix is simply to wrap the second call to txtOut.appendText(...) in another Platform.runLater():
Platform.runLater(() -> txtOut.appendText("\n Say 'Hello World'"));
This is guaranteed to work, because runnables passed to Platform.runLater() are guaranteed to be executed in the order in which they are passed, and the countdown latch establishes a "happens-before" relationship between the two calls to Platform.runLater().
Note however that you are blocking the FX Application Thread with the call to latch.await(), which is bad practice (and will delay the display of the stage until the background thread completes). You should really put the call to latch.await(), along with the second Platform.runLater() in another background thread. Also note that you don't really need the latch at all, as you already have a FutureTask, and you can just wait for its result (this will be equivalent to waiting for the latch). So you can do
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class JavafxLatchDemo1 extends Application {
#Override
public void start(Stage primaryStage) throws InterruptedException {
// CountDownLatch latch = new CountDownLatch(1);
TextArea txtOut = new TextArea();
StackPane root = new StackPane();
root.getChildren().add(txtOut);
Scene scene = new Scene(root, 300, 250);
//invoke rpc function
// Callable<Integer> fibCall = new fibCallable(latch, txtOut);
Callable<Integer> fibCall = new fibCallable(txtOut);
FutureTask<Integer> fibTask = new FutureTask<Integer>(fibCall);
Thread fibThread = new Thread(fibTask);
fibThread.start();
// latch.await(); //阻塞等待计数为0
new Thread(() -> {
try {
// wait for fibTask to complete:
fibTask.get();
// and now append text to text area,
// but this now must be done back on the FX Application Thread
Platform.runLater(() -> txtOut.appendText("\n Say 'Hello World'"));
} catch (Exception ignored) {
// ignore interruption: thread is exiting anyway....
}
}).start();
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
class fibCallable implements Callable<Integer>{
// CountDownLatch latch;
TextArea txtInput;
fibCallable(TextArea txtIn){
txtInput = txtIn;
}
#Override
public Integer call() throws Exception {
int temp1=1,temp2=0;
System.out.println("Client will pay money for eshop");
for(int i=0; i<10; i++){
temp1 = temp1 + temp2;
temp2 = temp1;
}
System.out.println("Client already decide to pay money for eshop");
Platform.runLater(()->{
txtInput.appendText("\nWhy, am I first?");
});
// latch.countDown(); //计数减1
return (new Integer(temp1));
}
}
}
Finally, note that JavaFX has a concurrency API of its own, that supports various callbacks on the FX Application Thread directly. This API usually means you can avoid getting your hands dirty with latches and locks, etc.

Related

How can I change the scene By pressing a specific key(b) on the the keyboard?

In my application, there are two scenes: mainScene and bossScene where mainScene is used when starting up the application.
I'm trying to implement the boss key functionality where by pressing the 'b' key on the the keyboard should change the scene to bossScene. And also by pressing the button in bossScene should switch back to mainScene.
I'm getting an error on InteliJ saying "Cannot resolve method setOnKeyPressed in List
My Code:
public void start(Stage stage) throws Exception {
stage.setTitle("BossKey Example");
// Scene and layout for the main view
VBox root = new VBox();
Scene mainScene = new Scene(root, 500, 300);
// Scene for the BOSS view
Scene bossScene = new Scene(new Label("Nothing suspicious here"), 500, 300);
List<TextField> fields = new ArrayList<TextField>();
for (int i = 0; i < 10; i++) {
fields.add(new TextField());
}
fields.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent keyEvent) {
switch (keyEvent.getCharacter()){
case "b": stage.setScene(bossScene); break;
}
}
});
/////// Added addEventFilter, still not working
mainScene.addEventFilter(KeyEvent.KEY_PRESSED, new
EventHandler<KeyEvent() {
#Override
public void handle(KeyEvent keyEvent) {
switch (keyEvent.getCharacter()){
case "b": stage.setScene(bossScene); break;
}
keyEvent.consume();
}
});
// Create components for main view
root.getChildren().addAll(fields);
root.getChildren().add(new Button("Hello!"));
stage.setScene(mainScene);
stage.show();
}
}
KeyCombination filters
You should use a key combination in an event filter, e.g., CTRL+B or SHORTCUT+B.
For details on how to apply key combinations, see:
javafx keyboard event shortcut key
Why a key combination is superior to filtering on the character "b":
If you filter on a "b" character, the feature won't work if caps lock is down.
If you filter on a "b" character, you will be unable to type "b" in the text field.
You might think you could write scene.setOnKeyPressed(...), however, that won't work as expected in many cases. A filter is required rather than a key press event handler because the key events may be consumed by focused fields like text fields if you use a handler, so a handler implementation might not activate in all desired cases.
Filtering on a key combination avoids the issues with trying to handle a character key press. The key combinations rely on key codes which represent the physical key pressed and don't rely on the state of other keys such as caps lock unless you explicitly add additional logic for that.
If you don't understand the difference between an event filter and an event handler and the capturing and bubbling phases of event dispatch, then study:
the oracle event handling tutorial.
KeyCombination filter implementation
final EventHandler<KeyEvent> bossEventFilter = new EventHandler<>() {
final KeyCombination bossKeyCombo = new KeyCodeCombination(
KeyCode.B,
KeyCombination.CONTROL_DOWN
);
public void handle(KeyEvent e) {
if (bossKeyCombo.match(e)) {
if (stage.getScene() == mainScene) {
stage.setScene(bossScene);
} else if (stage.getScene() == bossScene) {
stage.setScene(mainScene);
}
e.consume();
}
}
};
mainScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);
bossScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);
Accelerator alternative
An accelerator could be used instead of an event filter. Information on applying an accelerator is also in an answer to the linked question, I won't detail this alternative further here.
Example Solution
Standalone executable example code:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.io.IOException;
public class SceneSwap extends Application {
#Override
public void start(Stage stage) throws IOException {
final Scene mainScene = new Scene(
createLayout(
"Press CTRL+B to enter boss mode",
Color.PALEGREEN
)
);
final Scene bossScene = new Scene(
createLayout(
"Press CTRL+B to exit boss mode",
Color.PALEGOLDENROD
)
);
final EventHandler<KeyEvent> bossEventFilter = new EventHandler<>() {
final KeyCombination bossKeyCombo = new KeyCodeCombination(
KeyCode.B,
KeyCombination.CONTROL_DOWN
);
public void handle(KeyEvent e) {
if (bossKeyCombo.match(e)) {
if (stage.getScene() == mainScene) {
stage.setScene(bossScene);
} else if (stage.getScene() == bossScene) {
stage.setScene(mainScene);
}
e.consume();
}
}
};
mainScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);
bossScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);
stage.setScene(mainScene);
stage.show();
}
private VBox createLayout(String text, Color color) {
VBox mainLayout = new VBox(10,
new Label(text),
new TextField()
);
mainLayout.setPadding(new Insets(10));
mainLayout.setStyle("-fx-background: " + toCssColor(color));
return mainLayout;
}
private String toCssColor(Color color) {
int r = (int) Math.round(color.getRed() * 255.0);
int g = (int) Math.round(color.getGreen() * 255.0);
int b = (int) Math.round(color.getBlue() * 255.0);
int o = (int) Math.round(color.getOpacity() * 255.0);
return String.format("#%02x%02x%02x%02x" , r, g, b, o);
}
public static void main(String[] args) {
launch();
}
}

Updating two text objects in JavaFX, one field after another, only seeing final result of both changes

I have three text fields displayed, and I want to change the second one, see the result on the display (so wait a couple of seconds), then change the third one, and see the result on the display. Instead, I only see the result of both changes on the display (with no pause inbetween).
import javafx.application.Application;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.animation.PauseTransition;
import javafx.util.Duration;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
public class TestApp extends Application
{
private Text tone = new Text("one");
private Text ttwo = new Text("two");
private Text tthree = new Text("three");
private void process()
{
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event -> ttwo.setText("four"));
pauseTransition.play();
pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event -> tthree.setText("five"));
pauseTransition.play();
} // end of method "process"
#Override
public void start(Stage stage)
{
VBox vboxRoot = new VBox();
vboxRoot.getChildren().add(tone);
vboxRoot.getChildren().add(ttwo);
vboxRoot.getChildren().add(tthree);
Scene myScene = new Scene(vboxRoot,350,350);
stage.setScene(myScene);
stage.setTitle("Test");
stage.show();
process();
} // end of method "start"
} // end of class "TestApp"
So initially
one
two
three
is displayed; followed by
one
four
five
What I want to see is
one
four
three
a pause and then
one
four
five
I'm not sure if its a typo if your What I want to see is but if its not the reason you are getting
one
two
three
to initially display is because thats what you have them set as and in this piece of code below you setup 2 PauseTransitions that both have a 2 second wait before changing the text
private void process()
{
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event -> ttwo.setText("four"));
pauseTransition.play();
pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event -> tthree.setText("five"));
pauseTransition.play();
}
To fix this you can do a few things such as
Appropriately set what you want from the start
Run ttwo.setText("four"); at the start of your process() method
After doing that you get the starting result of
one
four
three
and after the pause transition finishes 2 seconds later you will see
one
four
five
After pauseTransition.play(); you assign new value to pauseTransition and you play it again, long before the first one completes.
A better approach would be :
Introduce a counter field : private int counter = 0;
And use it like so:
private void process() {
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event ->{
show(counter++);
if(counter < 5 ) pauseTransition.play();//stop criteria
});
pauseTransition.play();
}
private void show(int counter) {
//respond based on counter
}
The following is mcve the demonstrates the idea (it is not meant to demostrate the exact behavior you want which is not clear to me) :
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FxMain extends Application {
private int counter = 0;
private final Text tone = new Text("one"),
ttwo = new Text("two"),
tthree = new Text("three");
#Override
public void start(Stage primaryStage) throws Exception{
VBox root = new VBox(10);
root.setPadding(new Insets(10));
root.getChildren().addAll(tone,ttwo,tthree);
primaryStage.setScene(new Scene(root, 100,100));
primaryStage.sizeToScene();
primaryStage.show();
process();
}
private void process() {
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event ->{
show(counter++);
if(counter < 3 ) {
pauseTransition.play();//stop criteria
}
});
pauseTransition.play();
}
private void show(int counter) {
switch(counter){
case 0:
tone.setText("two");
break;
case 1:
ttwo.setText("three");
break;
default :
tthree.setText("four");
break;
}
}
public static void main(final String[] args) {
launch(args);
}
}
As others have mentioned, you play both PauseTransitions virtually in parallel. They are started within milliseconds (if not nanoseconds) of each other and I would not be surprised if they actually completed in the same frame. Because of this you see the result of both animations simultaneously.
One solution is to use a SequentialTransition; it will play a list of animations in the order of said list.
private void process() {
SequentialTransition st = new SequentialTransition();
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event -> ttwo.setText("four"));
st.getChildren().add(pauseTransition);
pauseTransition = new PauseTransition(Duration.seconds(2));
pauseTransition.setOnFinished(event -> tthree.setText("five"));
st.getChildren().add(pauseTransition);
st.play();
}
Another solution is to use a Timeline made up of multiple KeyFrames configured to execute at increasing times.
private void process() {
new Timeline(
new KeyFrame(Duration.seconds(2), event -> ttwo.setText("four")),
new KeyFrame(Duration.seconds(4), event -> tthree.setText("five"))
).play();
}
You mention your real goal may be more complicated than setting the text properties of some Text objects. You can adapt either solution to a more general purpose mechanism. Here's an example for a Timeline that will execute an arbitrary number of actions, with a fixed delay between each action (including after the last action):
private static Timeline createTimeline(Duration period, Runnable... actions) {
var frames = new ArrayList<KeyFrame>(actions.length + 1);
var time = Duration.ZERO;
for (var action : actions) {
frames.add(new KeyFrame(time, event -> action.run()));
time = time.add(period);
}
frames.add(new KeyFrame(time)); // adds a delay after last action
return new Timeline(frames.toArray(KeyFrame[]::new));
}
But what happens if I use your approach for one set of actions; and then move onto something else that generates another set of actions. How can I be sure the first set of actions has completed (i.e. been displayed) before I start the second set?
You can use the Animation#onFinished property, in combination with a Queue<Animation>, to play the next Animation when the previous one completes.
private final Queue<Animation> animationQueue = ...;
private Animation currentAnimation;
private void playAnimation(Animation animation) {
if (animation.getCycleCount() == Animation.INDEFINITE) {
throw new IllegalArgumentException();
}
animation.setOnFinished(event -> {
currentAnimation = animationQueue.poll();
if (currentAnimation != null) {
currentAnimation.playFromStart();
}
});
if (currentAnimation != null) {
animationQueue.add(animation);
} else {
currentAnimation = animation;
animation.playFromStart();
}
}

Background thread directly accessing UI anyway

Here is my code, can someone explain why it works every time?
package dingding;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Dingding extends Application {
TextField tfAuto = new TextField("0");
AutoRunThread runner = new AutoRunThread();
boolean shouldStop = false;
private class AutoRunThread extends Thread {
#Override
public void run() {
while (true) {
int i = Integer.parseInt(tfAuto.getText());
++i;
tfAuto.setText(String.valueOf(i));
try {
Thread.sleep(1000);
} catch (Throwable t) {
}
if (shouldStop) {
runner = null;
shouldStop = false;
return;
}
}
}
}
#Override
public void start(Stage primaryStage) {
Button btnStart = new Button("Increment Automatically");
Button btnStop = new Button("Stop Autotask");
btnStart.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (runner == null) {
runner = new AutoRunThread();
runner.setDaemon(true);
}
if (runner != null && !(runner.isAlive())) {
runner.start();
}
}
});
btnStop.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
shouldStop = true;
}
});
VBox rootBox = new VBox();
HBox autoBox = new HBox();
autoBox.getChildren().addAll(tfAuto, btnStart, btnStop);
rootBox.getChildren().addAll(autoBox);
Scene scene = new Scene(rootBox, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
As I said in my comment, improperly synchronized code doesn't guarantee errors per se. However, that doesn't mean said code, when used in a multi-threaded context, is actually working—you're merely getting lucky. Eventually you'll run into undefined behavior such as corrupted state, stale values, and unexpected exceptions. This is because, without synchronization, actions performed by one thread are not guaranteed to be visible to any other thread. You need a happens-before relationship, better described in the package documentation of java.util.concurrent and this SO question.
JavaFX, like most UI frameworks/toolkits, is single threaded. This means there's a special thread—in this case, the JavaFX Application Thread— that is responsible for all UI related actions1. It is this thread, and this thread only, that must be used to access and/or modify state related to a "live" scene graph (i.e. nodes that are in a scene that's in a window that's showing2). Using any other thread can lead to the undefined behavior described above.
Some UI related functions actually ensure they're being called on the JavaFX Application Thread, usually throwing an IllegalStateException if not. However, the remaining functions will silently let you call them from any thread—but that doesn't mean it's safe to do so. This is done this way, I believe, because checking the thread in every UI related function is a maintenance nightmare and would incur a not-insignificant performance cost.
1. It's slightly more complicated that this; JavaFX also has a "prism render thread" and a "media thread". See Understanding JavaFX Architecture for more information. But note that, from an application developer's point of view, the only thread that matters is the JavaFX Application Thread.
2. This is documented by Node. Note that some nodes, such as WebView, are more restrictive when it comes to threading; this will be documented in the appropriate places.

JavaFX2 play wav in background

So I'm trying to play indefinitly a song on a background thread, but when the music ends it does not loop as it was supose to.
Tried the offered solution but yet no joy! Here is the code for the main class, hope this helps in the resolution of the issue.
Even tried to loop the thread, but no joy...
Not sure why it's ending after playing the full file once, but not sure how to solve it!
Here is the code I have. Any help is welcome
public class Main extends Application {
Media sugar = new Media(this.getClass().getResource("sounds/t1coSugar.wav").toExternalForm());
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(Main.class, args);
}
#Override
public void start(Stage primaryStage)
{
primaryStage.setTitle("pacman");
primaryStage.setWidth(MazeData.calcGridX(MazeData.GRID_SIZE_X + 2)); //stage size x
primaryStage.setHeight(MazeData.calcGridY(MazeData.GRID_SIZE_Y + 5)); //stage size y
//splash screen
//end of splash screen
final Group root = new Group();
final Scene scene = new Scene(root);
root.getChildren().add(new Maze());
primaryStage.setScene(scene);
primaryStage.show();
int playbackgroundmusic = playbackgroundmusic();
}
private int playbackgroundmusic()
{
Runnable task = new Runnable() {
#Override
public void run() {
playSugar(); //method of the music
}
};
// Run the task in a background thread
Thread backgroundThread = new Thread(task);
// Terminate the running thread if the application exits
backgroundThread.setDaemon(true);
// Start the thread
backgroundThread.start();
return 0;
}
public void playSugar()
{
MediaPlayer mediaplayer = new MediaPlayer(sugar);
mediaplayer.volumeProperty().setValue(0.4);
mediaplayer.setStartTime(Duration.seconds(0));
mediaplayer.setStopTime(Duration.seconds(67));
mediaplayer.setAutoPlay(true);
mediaplayer.setCycleCount(MediaPlayer.INDEFINITE);
mediaplayer.play();
}

Display Popup with ProgressBar in JavaFX

How can I display my progress bar through pop up and automatically close if process is finished. Here is my code.
Task<ProgressForm> task = new Task<ProgressForm>() {
#Override
public ProgressForm call() throws InterruptedException{
ProgressForm pf = new ProgressForm();
for (int i = 1; i <= 10; i++) {
pf.activateProgressBar(this);
updateProgress(i, 10);
}
return pf;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
ProgressForm pf = (ProgressForm)task.getValue();
pf.getDialogStage().close();
}
});
Thread th = new Thread(task);
th.run();
Progress form class:
private final Stage dialogStage;
private final ProgressBar pb = new ProgressBar();
private final ProgressIndicator pin = new ProgressIndicator();
public ProgressForm() {
dialogStage = new Stage();
dialogStage.initStyle(StageStyle.UTILITY);
dialogStage.setResizable(false);
dialogStage.initModality(Modality.APPLICATION_MODAL);
// PROGRESS BAR
final Label label = new Label();
label.setText("alerto");
pb.setProgress(-1F);
pin.setProgress(-1F);
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(pb, pin);
Scene scene = new Scene(hb);
dialogStage.setScene(scene);
}
public void activateProgressBar(final Task task) throws InterruptedException {
pb.progressProperty().bind(task.progressProperty());
pin.progressProperty().bind(task.progressProperty());
dialogStage.show();
}
public Stage getDialogStage() {
return dialogStage;
}
The problem with this code is
if i use .show(), displaying pop up is smooth but NO PROGRESS BAR.
if i use .showAndWait(), displaying pop up requires manual exit for the pop up to close BUT Progress bar displays.
Any thoughts/ideas about this?
The two rules for multithreading in JavaFX are:
Code which modifies the UI (creates a Stage or changes properties
of nodes that are part of a scene graph) must be executed on the
JavaFX Application thread. Violating this rule will either throw
IllegalStateExceptions or result in unpredictable behavior.
Code which takes a long time to execute should be executed in a background thread (i.e. not the FX Application Thread). Violating this rule will cause the UI to become unresponsive.
Your code violates the first rule, because it calls the ProgressForm constructor in a background thread. You should set up the UI first, show the dialog, and then start the background thread.
Note that there is no need to repeatedly bind the progress properties of the progress bar and indicator to the progress property of the task. Once it is bound, it will remain bound until and unless you unbind it.
It's quite hard to fix your code as it stands, because your background task doesn't actually do anything that takes any time. Here's a version of what you're doing with just a pause:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class ProgressDialogExample extends Application {
#Override
public void start(Stage primaryStage) {
Button startButton = new Button("Start");
startButton.setOnAction(e -> {
ProgressForm pForm = new ProgressForm();
// In real life this task would do something useful and return
// some meaningful result:
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws InterruptedException {
for (int i = 0; i < 10; i++) {
updateProgress(i, 10);
Thread.sleep(200);
}
updateProgress(10, 10);
return null ;
}
};
// binds progress of progress bars to progress of task:
pForm.activateProgressBar(task);
// in real life this method would get the result of the task
// and update the UI based on its value:
task.setOnSucceeded(event -> {
pForm.getDialogStage().close();
startButton.setDisable(false);
});
startButton.setDisable(true);
pForm.getDialogStage().show();
Thread thread = new Thread(task);
thread.start();
});
StackPane root = new StackPane(startButton);
Scene scene = new Scene(root, 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class ProgressForm {
private final Stage dialogStage;
private final ProgressBar pb = new ProgressBar();
private final ProgressIndicator pin = new ProgressIndicator();
public ProgressForm() {
dialogStage = new Stage();
dialogStage.initStyle(StageStyle.UTILITY);
dialogStage.setResizable(false);
dialogStage.initModality(Modality.APPLICATION_MODAL);
// PROGRESS BAR
final Label label = new Label();
label.setText("alerto");
pb.setProgress(-1F);
pin.setProgress(-1F);
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(pb, pin);
Scene scene = new Scene(hb);
dialogStage.setScene(scene);
}
public void activateProgressBar(final Task<?> task) {
pb.progressProperty().bind(task.progressProperty());
pin.progressProperty().bind(task.progressProperty());
dialogStage.show();
}
public Stage getDialogStage() {
return dialogStage;
}
}
public static void main(String[] args) {
launch(args);
}
}
You can use controlsfx library to display this easily
private void progressDialogue(){
copyWorker = createWorker();
ProgressDialog dialog = new ProgressDialog(copyWorker);
dialog.initStyle(StageStyle.TRANSPARENT);
dialog.setGraphic(null);
//stage.initStyle(StageStyle.TRANSPARENT);
dialog.initStyle(StageStyle.TRANSPARENT);
//dialog.setContentText("Files are Uploading");
//dialog.setTitle("Files Uploading");
//dialog.setHeaderText("This is demo");
dialog.setHeaderText(null);
dialog.setGraphic(null);
dialog.initStyle(StageStyle.UTILITY);
new Thread(copyWorker).start();
dialog.showAndWait();
}
public Task createWorker() {
return new Task() {
#Override
protected Object call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
updateMessage("2000 milliseconds");
updateProgress(i + 1, 10);
}
return true;
}
};
}
now you need to call the method progressDialogue();
the code is from this video : https://www.youtube.com/watch?v=DK_1YGLI9ig

Resources