I am trying to create multiple stages using different classes, whereby I can have another window being brought up by a click of a button, but this window should be in a different class.
I used to do this in Java where I would create an object of the class in the buttons action and use the name of the object to set the new JFrame visible, but modal to the main JFrame. I tried the same in JavaFX but it failed to work.
I have two different classes and both are in different stages, but I just can't use one stage to display the other stages. I only know to use one class whereby I would create another stage in the action handler method, but this makes the code very long and too complicated.
P.S. what I am trying to accomplish is not multiple screens in the same window. but different windows (stages), and I prefer not to use FXML files, but java files using netbeans.
Any help would be greatly appreciated.
So you want each class to be a sub-class of a Stage. I'll give you two Stages and how to interact with each other.
public class FirstStage extends Stage{
Button openOther = new Button("Open other Stage");
HBox x = new HBox();
FirstStage(){
x.getChildren().add(openOther);
this.setScene(new Scene(x, 300, 300));
this.show();
openOther.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
new SecondStage();
}//end action
});
}
}
For the second Stage,
public class SecondStage extends Stage {
Label x = new Label("Second stage");
VBox y = new VBox();
SecondStage(){
y.getChildren().add(x);
this.setScene(new Scene(y, 300, 300));
this.show();
}
}
And call from main the first stage:
#Override
public void start(Stage primaryStage){
new FirstClass();
}
// My version of the 1st answer above.
package edu.dtcc.michael_javafx_2;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class HelloApplication extends Application {
public void start(Stage primaryStage) {
new FirstStage();
}
}
class FirstStage extends Stage{
Button openOther = new Button("Open other Stage");
HBox x = new HBox();
FirstStage(){
x.getChildren().add(openOther);
this.setScene(new Scene(x, 300, 300));
this.show();
openOther.setOnAction(t -> new SecondStage());
}
}
class SecondStage extends Stage {
Label x = new Label("Second stage");
VBox y = new VBox();
SecondStage(){
y.getChildren().add(x);
this.setScene(new Scene(y, 300, 300));
this.show();
}
}
Thank you. This has been a tough topic for me after weeks of FX work.
To execute:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
new FirstStage();
}
public static void main(String[] args) {
launch(args);
}
}
Related
Is there any event handler present in Java FX, if i close a window directly bt pressing [X] button on Top right side.
Which events gets fire in this case ?
Nothing is working so far , neither setOnHiding not setOnCloseRequest()
Please help.
Try this one
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class Main extends Application {
#Override
public void start(Stage stage) {
Text text = new Text("!");
text.setFont(new Font(40));
VBox box = new VBox();
box.getChildren().add(text);
final Scene scene = new Scene(box,300, 250);
scene.setFill(null);
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(WindowEvent we) {
System.out.println("Stage is closing");
}
});
stage.close();
}
public static void main(String[] args) {
launch(args);
}
}
Source Stage close event : Stage « JavaFX « Java
I am trying to draw random circles with random x/y centers, but the result of my code is only one circle at the center of the stage (window).
I use Task class to update my UI every 1 second.
This is my code:
package javafxupdateui;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class JavaFXUpdateUI extends Application {
private Stage window;
private StackPane layout;
private Scene scene;
#Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("JavaFX - Update UI");
layout = new StackPane();
scene = new Scene(layout, 500, 500);
window.setScene(scene);
window.show();
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(new Runnable() {
#Override
public void run() {
drawCircles();
}
});
Thread.sleep(1000);
}
}
};
public void drawCircles() {
Circle circle;
float x = (float)(Math.random()*501);
float y = (float)(Math.random()*501);
circle = new Circle(x, y, 25, Color.RED);
layout.getChildren().add(circle);
scene.setRoot(layout);
window.setScene(scene);
}
public static void main(String[] args) {
launch(args);
}
}
The result of the above code is:
Result GUI
What is going wrong
StackPane is a layout pane, it centers everything by default. As you want to manually place the circles at random locations, you don't want to use a pane which manages the layout for you.
How to fix it
Use a Pane or a Group instead of StackPane. Neither Pane nor Group manage the layout of items for you, so children you add to them at specific locations will remain at those locations.
Aside
You might wish to use a Timeline for your periodic updates rather than a Task with runLater (though the later will still work OK, with a Timeline you don't have to deal with additional complexities of concurrent code).
I have a JavaFX application in which I have a main window (stage) from which the user should open a second, child, window. In the second window the use should do some things which will affect the application behavior later on.
I couldn't really understand how should I access the second screen's data/values after the user closes this screen.
This is an example of a similar situation. Let's say I want to get the second stage's text box value.
package multiplestagesexample;
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.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
/**
*
* #author IdoG
*/
public class MultipleStagesExample extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("New Stage");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
new CreateStage();
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Main Stage");
primaryStage.setScene(scene);
primaryStage.show();
// Get the TextField value from the Additional Stage ???
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
class CreateStage {
public CreateStage() {
TextField textBox = new TextField();
StackPane root = new StackPane();
root.getChildren().add(textBox);
Scene scene = new Scene(root, 300, 250);
Stage stage = new Stage();
stage.setTitle("Additional Stage");
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
}
}
This is a section of the relevant "real" code:
#FXML
private void handleOpenMappingToolWindowAction(ActionEvent event) throws IOException {
//mappingWindow is an instance variable
//Stage mappingWindow;
// gets a Sheet object that will be used in the "MappingToolWindow" stage
selectedSheet = (Sheet) sheetsBox.getSelectionModel().getSelectedItem();
FXMLLoader loader = new FXMLLoader(getClass().getResource("MappingToolWindow.fxml"));
Parent root = (Parent)loader.load();
MappingToolWindowController controller = loader.getController();
if (mappingWindow == null) {
Scene mappingScene = new Scene(root);
mappingWindow = new Stage();
mappingWindow.initModality(Modality.APPLICATION_MODAL);
mappingWindow.setTitle("Mapping Tool");
mappingWindow.setScene(mappingScene);
controller.setSheet(selectedSheet);
}
mappingWindow.showAndWait();
// ACCESS THE NEW STAGE ???
}
Make the text field an instance variable, so that you can define a method to access its text:
class CreateStage {
private TextField textBox ;
public CreateStage() {
textBox = new TextField();
StackPane root = new StackPane();
root.getChildren().add(textBox);
Scene scene = new Scene(root, 300, 250);
Stage stage = new Stage();
stage.setTitle("Additional Stage");
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
}
public String getText() {
return textBox.getText();
}
}
Now of course you can just do
CreateStage dialog = new CreateStage() ;
System.out.println(dialog.getText());
in your handler.
public class XXXX extends Window {
private static Stage popupstage;
private Person aperson;
public XXXX (Person a) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("../UserInterface/UI_XXXXX.fxml"));
popupstage = new Stage();
popupstage.setTitle(a.getAbc());
popupstage.initModality(Modality.APPLICATION_MODAL);
popupstage.setScene(new Scene(root,400,400));
}
public Stage getPopupstage() {
return popupstage;
}
}
Have a window that takes a parameter. Your window will then read the object passed. In the above example, the person object is read and it populate the title of the stage (new window).
I'm trying to find a way to update the Categories of a JavaFX CategoryAxis(). I made an observable list of the categories and they also do update in the plot() function. However, if I try to add a new item to the series, I get a java.lang.IllegalStateException. Although I know, that not a state is causing the error, moreover the dynamic adding seems to be the problem. Below I attached my Code.
package application;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
final XYChart.Series series1 = new XYChart.Series();
final XYChart.Series series2 = new XYChart.Series();
ObservableList<XYChart.Data> xyList1 = FXCollections.observableArrayList();
ObservableList<XYChart.Data> xyList2 = FXCollections.observableArrayList();
ObservableList<String> myXaxis = FXCollections.observableArrayList();
int i;
#Override public void start(Stage stage) {
stage.setTitle("Line Chart Sample");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Month");
final StackedBarChart<String,Number> lineChart =
new StackedBarChart<String,Number>(xAxis,yAxis);
lineChart.setTitle("Woohoo, 2010");
lineChart.setAnimated(false);
series1.setName("Test 1");
series2.setName("test 2");
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
plot();
}
}, 0, 1000);
Scene scene = new Scene(lineChart,800,600);
xAxis.setCategories(myXaxis);
XYChart.Series XYSeries1 = new XYChart.Series(xyList1);
XYChart.Series XYSeries2 = new XYChart.Series(xyList2);
lineChart.getData().addAll(XYSeries1,XYSeries2);
i = 0;
stage.setScene(scene);
stage.show();
}
public void plot() {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Date date = new Date();
date.setTime(date.getTime() + i++ * 11111);
myXaxis.add(dateFormat.format(date));
System.out.println(myXaxis);
// with the line below uncommented the application breaks. Without the x-axis is updated as intended.
//xyList1.add(new XYChart.Data(dateFormat.format(date), Math.random()*10));
}
public static void main(String[] args) {
launch(args);
}
}
Issue - Transitive Modification of Scene Graph Nodes off of the JavaFX Application Thread
Don't modify scene nodes (or even observable lists of data on which scene nodes depend), off of the main JavaFX application thread (it's illegal - as the IllegalStateException you received states).
A Timer thread does not run things on the JavaFX application thread.
Potential Fixes
There are a few ways to fix this:
Continue using a Timer, but surround the plot() call in the timer with Platform.runLater.
Use the JavaFX animation framework (the Timeline), which always runs all of it's code on the JavaFX application thread.
Of the two options, I think I'd prefer the second, but either will work.
Timer style solution
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override public void run() {
plot();
}
});
}
}, 0, 1000);
Timeline style solution
Timeline timeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
plot();
}
}
),
new KeyFrame(
Duration.seconds(1)
)
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
Alternative Service based solution
I suggest that you read up on concurrency in JavaFX.
Another alternative would be to use Task or Service in conjunction with a ScheduledExecutorService and updates running via Platform.runLater(). However this kind of solution, while sophisticated and flexible, is more complicated than the problem warrants as you described it and the simple Timeline or Timer based solutions should be preferred. The more complicated Service based solution is appropriate if each pulse results in the execution of a time consuming algorithm or a lot of network or file based I/O.
How to implement something kinda internal frame in JavaFx 2.0 specifically?
My attempt is as so..
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
ConnectDb connection;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
final Stage stage1 = new Stage();
StackPane pane = new StackPane();
Button btn = new Button("Click Me");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
connection = new ConnectDb();
try {
connection.start(stage1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Fire some thing..");
}
});
pane.getChildren().add(btn);
stage.setScene(new Scene(pane ,200, 300));
stage.show();
}
}
ConnectDb.java
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ConnectDb extends Application {
#Override
public void start(Stage stage) throws Exception {
StackPane pane = new StackPane();
Button btn = new Button("Click On Button which is me");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Something here..");
}
});
pane.getChildren().add(btn);
stage.setScene(new Scene(pane ,200, 300));
stage.show();
}
}
First of all, for your approach, you don't really need to (and therefore should not) extend ConnectDb from Application as you just use the start method to create new stages. You just need one Application class (in your case Main). You could just as well create the new stage/scene in your first event handler.
Secondly, there is no real MDI support in JavaFX 2.1. Right now, you can just have multiple Stages (which is the equivalent to having multiple windows/frames). But you cannot have something like an internal frame in a desktop pane.
I guess you could take the following actions:
Just use multiple Stages (windows) with the drawback that they will float quite uninspiredly on your desktop
Use Swing as a container (with JDesktopPane and JInternalFrame) and integrate JavaFX (here's a nice How-To)
Implement your own framework that emulates MDI behavior
Find a framework that provides MDI behavior
Wait for a future release of JavaFX that hopefully provides MDI support (as far as I know, there's a change request pending...)
Create parent AncorPane.
Add several children AnchorPanes to it. They will serve as internal frames. Add different content to these.
Set children AnchorPanes invisible.
Add buttons to hide, resize or close children AnchorPanes. When needed, call function to set all children AnchorPanes invisible, except for one.