How to pause javafx class - java

I am building an alarm and it consists of two parts
an animated button created in javafx class and the engine which is created normally
what I need is whenever user press the animated button that closes the button and fire up the engine then after the engine is closed there will be some time then animated button appears again and so on
so I used ::
notify_me.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
new engine();
Platform.exit();
}
});
and in order to repeat this process I used
Timer t = new Timer(0,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
while(true){
javafx.launch(javafx.class);
//some extra code goes here including sleep for
//some time and check for engine window state
}
}
});
t.start();
but I am facing two problems:
some extra code isn`t implemented until platform is exited,
launch() cannot be called more than once
so how can I achieve that without using threads ?? thanks

You probably won't get around using Threads. I'd recommend not shutting down the fx application thread however. Just close all windows and show (some of) them again after the delay:
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Hide me 5 sec");
// prevent automatic exit of application when last window is closed
Platform.setImplicitExit(false);
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
// timer should be a daemon (-> not prevent jvm shutdown)
Timer timer = new Timer(true);
btn.setOnAction((ActionEvent event) -> {
timer.schedule(new TimerTask() {
#Override
public void run() {
// make window reappear (needs to happen on the application thread)
Platform.runLater(primaryStage::show);
}
}, 5000l);
// hide window
primaryStage.close();
});
// allow exiting the application by clicking the X
primaryStage.setOnCloseRequest(evt -> Platform.exit());
primaryStage.show();
}

Related

Use of Thread.sleep() in JavaFx Application

I try to make an application that shows a button. When I click the button, the scene should show some text (add a label) for a few seconds, and then the text should disappear (removing the label from the scene). But in fact when I click the button, nothing happens.
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
StackPane pane = new StackPane();
Button btn = new Button();
Label lb = new Label("Start");
pane.getChildren().addAll(btn);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
btn.setOnAction(e->{
pane.getChildren().addAll(lb);
try {
Thread.sleep(300);
} catch (Exception ex) {
ex.printStackTrace();
}
pane.getChildren().removeAll(lb);
});
}
public static void main(String[] args) {
launch(args);
}
}
Use a PauseTransition:
btn.setOnAction(e->{
pane.getChildren().addAll(lb);
PauseTransition pause = new PauseTransition(Duration.seconds(0.3));
pause.setOnFinished(e -> pane.getChildren().removeAll(lb));
pause.play();
});
The reason your approach doesn't work, is that JavaFX effectively uses a single thread for rendering and handling user events. It will update the screen at a fixed interval (60 times per second in the current implementation), but for synchronization reasons has to wait for any pending events that are currently being handled to complete first. So you original code adds the label, pauses for 0.3 seconds, and then removes the label. The FX Application Thread is occupied for this whole process, so the FX framework never has an opportunity to redraw the scene while it is happening.
The bottom line here is that you should never block the FX application thread, by calling sleep() or by executing long-running operations.
Update in response to additional question in comment:
To disable all event handling, you can call setDisable(true) on the root node of the scene. So to prevent event handling while the label is shown:
btn.setOnAction(e->{
pane.getChildren().addAll(lb);
pane.setDisable(true);
PauseTransition pause = new PauseTransition(Duration.seconds(0.3));
pause.setOnFinished(e -> {
pane.getChildren().removeAll(lb));
pane.setDisable(false);
});
pause.play();
});

java.lang.IllegalStateException: Application launch must not be called more than once - JavaFX (first once works, the 2nd non-) [duplicate]

How to call the launch() more than once in java i am given an exception as "ERROR IN MAIN:java.lang.IllegalStateException: Application launch must not be called more than once"
I have create rest cleint in my java application when request comes it call javafx and opening webview after completing webview operarion am closing javafx windows using Platform.exit() method. when second request comes am getting this error how to reslove this error.
JavaFx Application Code:
public class AppWebview extends Application {
public static Stage stage;
#Override
public void start(Stage _stage) throws Exception {
stage = _stage;
StackPane root = new StackPane();
WebView view = new WebView();
WebEngine engine = view.getEngine();
engine.load(PaymentServerRestAPI.BROWSER_URL);
root.getChildren().add(view);
engine.setJavaScriptEnabled(true);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
engine.setOnResized(new EventHandler<WebEvent<Rectangle2D>>() {
public void handle(WebEvent<Rectangle2D> ev) {
Rectangle2D r = ev.getData();
stage.setWidth(r.getWidth());
stage.setHeight(r.getHeight());
}
});
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("app", new BrowserApp());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
RestClient Method:
Calling to JavaFX application
// method 1 to lanch javafx
javafx.application.Application.launch(AppWebview.class);
// method 2 to lanch javafx
String[] arguments = new String[] {"123"};
AppWebview .main(arguments);
You can't call launch() on a JavaFX application more than once, it's not allowed.
From the javadoc:
It must not be called more than once or an exception will be thrown.
Suggestion for showing a window periodically
Just call Application.launch() once.
Keep the JavaFX runtime running in the background using Platform.setImplicitExit(false), so that JavaFX does not shutdown automatically when you hide the last application window.
The next time you need another window, wrap the window show() call in Platform.runLater(), so that the call gets executed on the JavaFX application thread.
For a short summary implementation of this approach:
See the answer by sergioFC
If you are mixing Swing you can use a JFXPanel instead of an Application, but the usage pattern will be similar to that outlined above.
For an example of the JFXPanel apprach, see Irshad Babar
s answer.
Wumpus Sample
This example is bit more complicated than it needs to be because it also involves timer tasks. However it does provide a complete stand-alone example, which might help sometimes.
import javafx.animation.PauseTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.*;
// hunt the Wumpus....
public class Wumpus extends Application {
private static final Insets SAFETY_ZONE = new Insets(10);
private Label cowerInFear = new Label();
private Stage mainStage;
#Override
public void start(final Stage stage) {
// wumpus rulez
mainStage = stage;
mainStage.setAlwaysOnTop(true);
// the wumpus doesn't leave when the last stage is hidden.
Platform.setImplicitExit(false);
// the savage Wumpus will attack
// in the background when we least expect
// (at regular intervals ;-).
Timer timer = new Timer();
timer.schedule(new WumpusAttack(), 0, 5_000);
// every time we cower in fear
// from the last savage attack
// the wumpus will hide two seconds later.
cowerInFear.setPadding(SAFETY_ZONE);
cowerInFear.textProperty().addListener((observable, oldValue, newValue) -> {
PauseTransition pause = new PauseTransition(
Duration.seconds(2)
);
pause.setOnFinished(event -> stage.hide());
pause.play();
});
// when we just can't take it anymore,
// a simple click will quiet the Wumpus,
// but you have to be quick...
cowerInFear.setOnMouseClicked(event -> {
timer.cancel();
Platform.exit();
});
stage.setScene(new Scene(cowerInFear));
}
// it's so scary...
public class WumpusAttack extends TimerTask {
private String[] attacks = {
"hugs you",
"reads you a bedtime story",
"sings you a lullaby",
"puts you to sleep"
};
// the restaurant at the end of the universe.
private Random random = new Random(42);
#Override
public void run() {
// use runlater when we mess with the scene graph,
// so we don't cross the streams, as that would be bad.
Platform.runLater(() -> {
cowerInFear.setText("The Wumpus " + nextAttack() + "!");
mainStage.sizeToScene();
mainStage.show();
});
}
private String nextAttack() {
return attacks[random.nextInt(attacks.length)];
}
}
public static void main(String[] args) {
launch(args);
}
}
Update, Jan 2020
Java 9 added a new feature called Platform.startup(), which you can use to trigger startup of the JavaFX runtime without defining a class derived from Application and calling launch() on it. Platform.startup() has similar restrictions to the launch() method (you cannot call Platform.startup() more than once), so the elements of how it can be applied is similar to the launch() discussion and Wumpus example in this answer.
For a demonstration on how Platform.startup() can be used, see Fabian's answer to How to achieve JavaFX and non-JavaFX interaction?
I use something like this, similar to other answers.
private static volatile boolean javaFxLaunched = false;
public static void myLaunch(Class<? extends Application> applicationClass) {
if (!javaFxLaunched) { // First time
Platform.setImplicitExit(false);
new Thread(()->Application.launch(applicationClass)).start();
javaFxLaunched = true;
} else { // Next times
Platform.runLater(()->{
try {
Application application = applicationClass.newInstance();
Stage primaryStage = new Stage();
application.start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
try this, I tried this and found successful
#Override
public void start() {
super.start();
try {
// Because we need to init the JavaFX toolkit - which usually Application.launch does
// I'm not sure if this way of launching has any effect on anything
new JFXPanel();
Platform.runLater(new Runnable() {
#Override
public void run() {
// Your class that extends Application
new ArtisanArmourerInterface().start(new Stage());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

My TimerTask Function cannot update the label.setText

Im writing a small program that involves a timer but for some reason I cant get the TimerTask to update the value of my Label.setText
public class Main extends Application {
Label TimerLabel;
/* Create a Button named button */
Button button;
/* Create three Radiobuttons named Radio1,Radio2,Radio3 */
RadioButton Radio1, Radio2, Radio3;
Timer QuestionTimer = new Timer();
TimerTask QuestionTick = new TimerTask() {
#Override
public void run() {
TimerLabel.setText(String.valueOf(Integer.valueOf(TimerLabel.getText())+1));
}
};
public static void main(String[] args) {
launch(args);
}
/*UI Part*/
#Override
public void start(Stage primaryStage) throws Exception {
/*Window(Stage) Title is set to "Try 1"*/
primaryStage.setTitle("Try 1");
/*Button properties*/
button = new Button();
button.setText("Click Me");
//Radio Button
Radio1 = new RadioButton();
Radio1.setText("Click Me 1");
Radio1.setOnAction(e->{
System.out.println("Radio Button Clicked");
});
Radio2 = new RadioButton();
Radio2.setText("Click Me 2");
Radio2.setOnAction(e->{
System.out.println("Radio Button 2 Clicked");
});
Radio3 = new RadioButton();
Radio3.setText("Click Me 3");
Radio3.setOnAction(e->{
System.out.println("Radio Button 3 Clicked");
});
TimerLabel = new Label();
TimerLabel.setText("0");
/*Here my layout is defined */
Pane Buttonlayout = new Pane();
button.setLayoutX(200);
button.setLayoutY(200);
Radio1.setLayoutX(15);
Radio1.setLayoutY(20);
Radio2.setLayoutX(15);
Radio2.setLayoutY(40);
Radio3.setLayoutX(15);
Radio3.setLayoutY(60);
TimerLabel.setLayoutX(100);
TimerLabel.setLayoutY(20);
Buttonlayout.getChildren().add(button);
Buttonlayout.getChildren().add(Radio1);
Buttonlayout.getChildren().add(Radio2);
Buttonlayout.getChildren().add(Radio3);
Buttonlayout.getChildren().add(TimerLabel);
/*Here we define the scene (aka everything inside the stage (inside the window))*/
Scene scene1 = new Scene(Buttonlayout,300,250);
primaryStage.setScene(scene1);
primaryStage.show();
QuestionTimer.scheduleAtFixedRate(QuestionTick,1000,1000);
}
}
So thats my code, I know most of it seems stupid but I first wanted to start programming on the timer and well it didnt work. Any kind of help would be appreciated
From the code it appears you are utilizing the java.util.Timer class. The documentation states Implementation note: All constructors start a timer thread. The the JavaFX UI should not be updated from another thread, which is what you are doing with the timer.
Rather than updating the UI directly use the Platform.runLater(Runnable) to schedule the UI tasks on the main JavaFX thread.
javafx.application.Platform.runLater(new Runnable() {
#Override
public void run() {
TimerLabel.setText(String.valueOf(Integer.valueOf(TimerLabel.getText())+1));
}
}
And please, do yourself a favor and get rid of the method chaining. It will make it easier to debug when Integer.valueOf throws an Exception.
String timer_label = TimerLabel.getText();
Integer timer_int = Integer.valueOf(timer_label);
String timer_text = String.valueOf(timer_int + 1);
TimerLabel.setText(timer_text);

How do I get the close event of a stage in JavaFX?

In JavaFX, how can I get the event if a user clicks the Close Button(X) (right most top cross) a stage?
I want my application to print a debug message when the window is closed. (System.out.println("Application Close by click to Close Button(X)"))
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
// Any Event Handler
//{
System.out.println("Application(primaryStage) Closed by click to Close Button(X)");
//}
}
I got the answer for this question
stage.setOnHiding(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println("Application Closed by click to Close Button(X)");
System.exit(0);
}
});
}
});
Another method for achieving the same effect, but remains more consistent with the way you start your application is to override stop();
According to the JavaFX documentation, the lifecycle of an instance of an Application is as follows:
The JavaFX runtime does the following, in order, whenever an application is launched:
Constructs an instance of the specified Application class
Calls the init() method
Calls the start(javafx.stage.Stage) method
Waits for the application to finish, which happens when either of the following occur:
the application calls Platform.exit()
the last window has been closed and the implicitExit attribute on Platform is true
Calls the stop() method
As a result you simply override stop()
#Override
public void stop(){
System.out.println("Stage is closing");
}
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(WindowEvent we) {
System.out.println("Stage is closing");
}
});

Handle mouse event anywhere with JavaFX

I have a JavaFX application, and I would like to add an event handler for a mouse click anywhere within the scene. The following approach works ok, but not exactly in the way I want to. Here is a sample to illustrate the problem:
public void start(Stage primaryStage) {
root = new AnchorPane();
scene = new Scene(root,500,200);
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse click detected! "+event.getSource());
}
});
Button button = new Button("click here");
root.getChildren().add(button);
primaryStage.setScene(scene);
primaryStage.show();
}
If I click anywhere in empty space, the EventHandler invokes the handle() method, but if i click the button, the handle() method is not invoked. There are many buttons and other interactive elements in my application, so I need an approach to catch clicks on those elements as well without having to manually add a new handler for every single element.
You can add an event filter to the scene with addEventFilter(). This will be called before the event is consumed by any child controls. Here's what the code for the event filter looks like.
scene.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("mouse click detected! " + mouseEvent.getSource());
}
});

Categories

Resources