lanching same JavaFx window after closing it - javafx

I am open A JavaFX application using a Jframe.And after using JavaFx application window i close the window .And want to again open this same Javafx window but an error occur-
java.lang.IllegalStateException: Application launch must not be called more than once

As stated in the documentation, calling Application.launch(...) more than once will result in an exception:
public static void launch(String... args)
Launch a standalone application. This method is typically called from
the main method(). It must not be called more than once or an
exception will be thrown.
If you need to mix Swing and JavaFX, you should embed the JavaFX pieces in a JFXPanel and place it in a JFrame. You can then show and hide the JFrame as often as you need, using setVisible(...). An application working this way will not have an Application subclass at all.
Mixing Swing and JavaFX is tricky, and not recommended for beginners. The problem is that each toolkit has its own UI thread, and all access of the UI must be executed on the correct UI thread (i.e. the AWT event dispatch thread for Swing/AWT components, and the JavaFX Application Thread for JavaFX components). Data that is shared between both must provide proper synchronization to ensure that it is safely accessible from multiple threads.
Here is a very simple example. Clicking the button will show the window with FX content. If you close that window, and then click the button again, it will be shown again.
import java.awt.BorderLayout;
import java.util.Random;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SwingFXExample {
private JFrame mainFrame ;
private JFrame fxFrame ;
private JFXPanel fxPanel ;
public SwingFXExample() {
// must be on Swing thread...
if (! SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Not on Event Dispatch Thread");
}
mainFrame = new JFrame();
JButton showFX = new JButton("Show FX Window");
JPanel content = new JPanel();
content.add(showFX);
mainFrame.add(content, BorderLayout.CENTER);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fxFrame = new JFrame();
fxPanel = new JFXPanel();
fxFrame.add(fxPanel);
fxFrame.setSize(640, 640);
fxFrame.setLocationRelativeTo(null);
Platform.runLater(() -> initFX());
showFX.addActionListener(event -> fxFrame.setVisible(true));
}
private void initFX() {
// must be on FX Application Thread...
if (! Platform.isFxApplicationThread()) {
throw new IllegalStateException("Not on FX Application Thread");
}
LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());
Series<Number, Number> series = new Series<>();
series.setName("Random data");
Random rng = new Random();
for (int i = 0; i <= 10; i++) {
series.getData().add( new Data<>(i, 100*rng.nextDouble()) );
}
chart.getData().add(series);
chart.setOnMouseClicked(evt -> {
if (evt.getClickCount() == 2) {
System.out.println("Double click!");
}
});
fxPanel.setScene(new Scene(chart, 400, 400));
}
public void showMainWindow() {
mainFrame.setSize(350, 120);
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SwingFXExample app = new SwingFXExample();
app.showMainWindow();
});
}
}

Related

JavaFX : How to close a sub window without getting focus on main window

I am trying to close a sub window programatically after certain time. This sub window initOwner is set with main stage. But on closing this sub window, the main window is getting focused. Is there any way to close the sub window(programatically) without gaining focus on main window?
Below is the quick demo of my issue. I tried all the possible ways to close the window. Steps to reproduce:
After starting the application, click the button to open the sub
window. This sub window will close automatically after 10seconds.
Meanwhile open any other application (notepad, outlook, browser.. or
whatever). While you are working on that application, when the sub
window is closed, the main stage gets focus and comes in front of my
current application. This is quite annoying to my client.
Note: I cannot remove initOwner(), as I always want to keep my sub window on top of the main window.
Update : Based on the comments, I tried running the demo with different jdk versions (u91, u121 & u211) and in Windows 10. In all three cases , the moment the sub window is closed, the main stage is coming to front. I even tried in a differnt system but the results are same :(
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
public class OwnerStage_Demo extends Application {
#Override
public void start(Stage stage) throws Exception {
Button button = new Button("Open Window");
button.setOnAction(e -> {
Stage stg = new Stage();
stg.setScene(new Scene(new StackPane(), 300, 300));
stg.initOwner(stage);
stg.show();
// Window will close automatically after 10secs.
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10000), x -> {
//stg.close();
//stg.hide();
stg.fireEvent(new WindowEvent(stg, WindowEvent.WINDOW_CLOSE_REQUEST));
}));
timeline.setCycleCount(1);
timeline.play();
});
VBox root = new VBox(button);
root.setSpacing(10);
Scene sc = new Scene(root, 600, 600);
stage.setScene(sc);
stage.show();
}
public static void main(String... a) {
Application.launch(a);
}
}
Update : Attached the gif demonstrating the issue.
How a window gains the focus depends on a platform (OS + JRE).
The platform processes focused window that is why the window may have different behavior on different OS after calling focus request.
There is no way to achieve required behaviour with pure JFX because of the restriction you had set:
Note: I cannot remove initOwner(), as I always want to keep my sub window on top of the main window.
com.sun.javafx.tk.quantum.WindowStage
if (!isPopupStage && owner != null && owner instanceof WindowStage) {
WindowStage ownerStage = (WindowStage)owner;
ownerStage.requestToFront();
}
What you can do is to imitate owner window <- child window relationship without initializing real owner.
Source:
public class PlainZStage extends Stage {
public PlainZStage(final Window owner) {
init(owner, this::focusedChanged);
}
private void init(final Window owner, final ChangeListener<Boolean> listener) {
showingProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(final ObservableValue<? extends Boolean> observable, final Boolean oldValue,
final Boolean newValue) {
owner.getScene().getRoot().setDisable(newValue);
if (newValue) {
owner.focusedProperty().addListener(listener);
} else {
owner.focusedProperty().removeListener(listener);
showingProperty().removeListener(this);
}
}
});
}
private void focusedChanged(final ObservableValue<? extends Boolean> source, final Boolean oldValue,
final Boolean newValue) {
if (newValue && isShowing()) {
toFront();
}
}
}
Usage:
button.setOnAction(e -> {
final Stage stg = new PlainZStage(stage);
stg.setScene(new Scene(new StackPane(), 300, 300));
stg.show();
// Window will close automatically after 10secs.
final Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10000), x -> {
stg.close();
}));
Alternatively, you could combine JFX&SWING to filter focus events but you will face pure architectural evil :)

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.

How can a dynamically generated JavaFX scene be changed in a single stage?

I'm currently working on a project requiring me to switch back and forward between scenes. I have already written some code for it but it isn't as elegant as I wanted to be, especially as there is few flickers when I switch between them and sometimes even the buttons that I have generated simply disappear only to appear once again when yet another scene has been generated. Moreover, the layout I'm using for my application isn't really fixed and I think that using FXML might be not suitable for what I'm doing.
Thank you.
This is what I'm using to change between scenes:
void changeScene(Stage stage,Scene scene){
stage.setScene(scene);
primaryStage.setFullScreen(true);
}
I assume by "switching between scenes" you mean that you want to change the entire content of the existing window.
There are two (very) slightly different ways you can do this. Either create a new Scene and pass it to the Stage's setScene(...) method. Or create the Parent that is the root of the new UI (either by FXML or otherwise), and pass it to the existing Scene's setRoot(...) method. There's no real advantage that I can see of one over the other.
Here's a minimal implementation of the second option. The UI is irrelevant to the question: the important parts are the event handlers for the "Login" button (which switches from the login scene to the main scene) and the "Logout" button (which switches back).
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
public class MinimalSceneSwitchingExample extends Application {
#Override
public void start(Stage primaryStage) {
LoginView loginView = new LoginView();
Scene scene = new Scene(loginView.getView(), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class LoginView {
private final IntegerProperty loginAttempts ;
private final GridPane view ;
public LoginView() {
view = new GridPane();
TextField usernameTF = new TextField("user");
TextField passwordTF = new TextField("pass");
// Login button switches to main view:
Button loginButton = new Button("Login");
loginButton.setOnAction(event -> {
if (usernameTF.getText().equalsIgnoreCase("user")
&& passwordTF.getText().equalsIgnoreCase("pass")) {
// *** Switch to main view: ***
Parent mainView = new MainView().getView();
view.getScene().setRoot(mainView);
} else {
loginAttempts.set(loginAttempts.get()+1);
}
});
// just set up login UI... irrelevant to this example:
loginAttempts = new SimpleIntegerProperty();
usernameTF.setPromptText("Hint: user");
passwordTF.setPromptText("Hint: pass");
view.addRow(0, new Label("Username:"), usernameTF);
view.addRow(1, new Label("Password:"), passwordTF);
Label loginErrorMessage = new Label();
loginErrorMessage.textProperty().bind(
Bindings.when(loginAttempts.isEqualTo(0))
.then("")
.otherwise(Bindings.format("Login incorrect (Attempts: %d)",
loginAttempts)));
view.add(loginErrorMessage, 0, 2, 2, 1);
view.add(loginButton, 0, 3, 2, 1);
ColumnConstraints leftCol = new ColumnConstraints();
leftCol.setHgrow(Priority.NEVER);
leftCol.setHalignment(HPos.RIGHT);
ColumnConstraints rightCol = new ColumnConstraints();
rightCol.setHgrow(Priority.ALWAYS);
rightCol.setHalignment(HPos.LEFT);
view.getColumnConstraints().addAll(leftCol, rightCol);
GridPane.setHalignment(loginErrorMessage, HPos.CENTER);
GridPane.setHalignment(loginButton, HPos.CENTER);
view.setHgap(10);
view.setVgap(16);
view.setAlignment(Pos.CENTER);
}
public Parent getView() {
return view ;
}
}
public static class MainView {
private BorderPane view ;
public MainView() {
view = new BorderPane();
// *** logout button switches back to a login view: ***
Button logoutButton = new Button("Log out");
logoutButton.setOnAction(event ->
view.getScene().setRoot(new LoginView().getView()));
// Arbitrary UI, irrelevant to this example:
SplitPane splitPane = new SplitPane();
ListView<String> listView = new ListView<>();
IntStream.rangeClosed(1, 10)
.mapToObj(Integer::toString)
.map("Item "::concat)
.forEach(listView.getItems()::add);
Label bigLabel = new Label();
bigLabel.textProperty().bind(
listView.getSelectionModel().selectedItemProperty());
bigLabel.setFont(Font.font("Verdana", FontWeight.BOLD, 18));
BorderPane.setAlignment(bigLabel, Pos.CENTER);
BorderPane.setMargin(bigLabel, new Insets(10));
Label details = new Label();
details.textProperty().bind(
Bindings.when(
listView.getSelectionModel().selectedItemProperty().isNull())
.then("")
.otherwise(Bindings.format("This is where you would display "
+ "all sorts of details about %1$s. "
+ "If %1$s were really a model object, you "
+ "might have a GridPane displaying all its "
+ "properties, for example.",
listView.getSelectionModel().selectedItemProperty())));
details.setWrapText(true);
BorderPane detailsPane = new BorderPane(details, bigLabel, null, null, null);
splitPane.getItems().addAll(listView, detailsPane);
view.setCenter(splitPane);
view.setBottom(logoutButton);
BorderPane.setAlignment(logoutButton, Pos.CENTER);
BorderPane.setMargin(logoutButton, new Insets(8));
BorderPane.setMargin(splitPane, new Insets(16));
}
public Parent getView() {
return view ;
}
}
public static void main(String[] args) {
launch(args);
}
}

How to add a shortcut event in javafx with combination of Ctrl + P +X

table.setOnKeyPressed(new EventHandler<KeyEvent>() {
// final KeyCombination kb = new KeyCodeCombination(KeyCode.P, KeyCombination.CONTROL_DOWN);
// final KeyCombination k = new KeyCodeCombina
public void handle(KeyEvent key) {
if (key.getCode() == KeyCode.P && key.isControlDown()) {
//My Code
}
}
});
I want to invoke the event with the shortcut keycombination of Ctrl+P+X
It is actually a little hard to understand what Ctrl+P+X means. I am going to assume it means that you press ctrl, then you press p, then you press x (potentially releasing the p before you press the x). I'll also assume that the order matters, e.g. press ctrl, then press x then press p would not count. Anyway a bit of speculation on my part, perhaps not exactly what you want, but hopefully you will get the gist of the provided solution and be able to adapt it to your situation.
The solution monitors both key presses and releases so that it can keep track of the state of key presses to determine if the key combination triggers.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.time.LocalTime;
public class KeyCombo extends Application {
KeyCombination ctrlP = KeyCodeCombination.keyCombination("Ctrl+P");
KeyCombination ctrlX = KeyCodeCombination.keyCombination("Ctrl+X");
#Override
public void start(Stage stage) throws Exception {
Label lastPressedLabel = new Label();
TextField textField = new TextField();
BooleanProperty pDown = new SimpleBooleanProperty(false);
textField.setOnKeyPressed(event -> {
if (ctrlP.match(event)) {
pDown.set(true);
}
if (pDown.get() && ctrlX.match(event)) {
pDown.set(false);
lastPressedLabel.setText(
LocalTime.now().toString()
);
}
});
textField.setOnKeyReleased(event -> {
if (!event.isControlDown()) {
pDown.set(false);
}
});
VBox layout = new VBox(10,
new Label("Press Ctrl+P+X"),
textField,
lastPressedLabel
);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you can, I'd advise trying to use a simpler control scheme, e.g. just Ctrl+P or Ctrl+X (which is directly supported by the key code combination event matching), rather than using a composite control scheme of Ctrl+P+X.

JavaFX + AWT Canvas

I read that running AWT With JavaFX is a bad idea.
But we have an old application that runs on Swing and uses the AWT canvas(Cannot change due to an external library that uses the canvas)
Is it really such a horrible idea?
Is there a workaround for this?
Update
Although the code in this answer used to work on Windows with an earlier version of JavaFX, I retested the same same code on OS X 10.9.5 + JavaFX 8u72 and the code no longer works.
The line swingNode.setContent(awtInitializerTask.get()); which instructs the JavaFX thread to wait on the awt thread to initialize the awt canvas never returns, blocking execution and startup of the app.
Just put your AWT canvas in a SwingNode and watch your thread management and you'll be fine.
import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javax.swing.*;
import java.awt.*;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class AwtCanvasWrapper extends Application {
private static final int W = 200;
private static final int H = 100;
#Override public void start(final Stage stage) throws Exception {
final AwtInitializerTask awtInitializerTask = new AwtInitializerTask(() -> {
JPanel jPanel = new JPanel();
jPanel.add(new CustomAwtCanvas(W, H));
return jPanel;
});
SwingUtilities.invokeLater(awtInitializerTask);
SwingNode swingNode = new SwingNode();
swingNode.setContent(awtInitializerTask.get());
stage.setScene(new Scene(new Group(swingNode), W, H));
stage.setResizable(false);
stage.show();
}
private class AwtInitializerTask extends FutureTask<JPanel> {
public AwtInitializerTask(Callable<JPanel> callable) {
super(callable);
}
}
private class CustomAwtCanvas extends Canvas {
public CustomAwtCanvas(int width, int height) {
setSize(width, height);
}
public void paint(Graphics g) {
Graphics2D g2;
g2 = (Graphics2D) g;
g2.setColor(Color.GRAY);
g2.fillRect(
0, 0,
(int) getSize().getWidth(), (int) getSize().getHeight()
);
g2.setColor(Color.BLACK);
g2.drawString("It is a custom canvas area", 25, 50);
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
Here is the output:
Related Question
Interoperability between Graphics2D and GraphicsContext
Answering some additional questions
But that one is for swing components.
Yes, but awt components can be wrapped in Swing components.
furthermore It says in the docs that it should not be used of heavyweight components
Regardless, it seems to work for me, your mileage may vary.
performance is crucial for my app
Then try the approach with your app and check:
The painting is reliable.
The performance is acceptable.
If either of the above checks fail then you may need to use a different approach (though I do not know what that approach would be, maybe just spawn Frame as a new window in which to include the the AWT canvas content rather than embedding the canvas inside the JavaFX scene).

Resources