How to create a javafx 2.0 application MDI - modal-dialog

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.

Related

Directly update button state JavaFX

I have a GUI that uploads a bunch of settings via serial once the 'upload' button is pressed.
This upload takes some time and has some Thread.sleep's in it, so during upload the GUI freezes but still allows the user to press the upload button some more, which results in even more freezing.
What would be the best way to directly disable the upload button, upload in the background, and enable the button when finished?
Thanks for the reply.
To answer my own question, I already found a simple solution by creating a task:
public class uploadTask extends Task<String> {
#Override
protected String call() throws Exception {
}
}
I would recommend using RxJavaFx.
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.rxjavafx.observables.JavaFxObservable;
import io.reactivex.rxjavafx.schedulers.JavaFxScheduler;
import io.reactivex.schedulers.Schedulers;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class BackgroundTaskButtonApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Button button = new Button("Run!");
StackPane stackPane = new StackPane(button);
Scene scene = new Scene(stackPane, 400, 400);
stage.setScene(scene);
stage.show();
JavaFxObservable.actionEventsOf(button)
.doOnNext(event -> button.setDisable(true))
.switchMap(event -> Observable.just(event).observeOn(Schedulers.single()).doOnNext(e -> runLongTask()))
.observeOn(JavaFxScheduler.platform())
.doOnNext(event -> button.setDisable(false))
.subscribe();
}
private void runLongTask() {
System.out.println(Thread.currentThread().getName() + " runLongTask()");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

ControlsFX HiddenSidesPane hides When clicked

I am using ControlsFX - HiddenSidesPane where i add some link(ToggleButtons) to be clicked for navigation.
The problem i have is whenever anything is clicked, the HiddenSiddesPane hides.
The desired behavior is when anything inside it is clicked it should not close/hide, unless cursor hovers out of it.
SSCCE to demonstrate undesired behavior
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.HiddenSidesPane;
public class MyHiddenSidesPaneDemo extends Application{
public static void main(String[] args) { Application.launch(args); }
#Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
TableView tv = new TableView();
HiddenSidesPane hiddenSidesPane = new HiddenSidesPane();
hiddenSidesPane.setContent(tv);
hiddenSidesPane.setLeft(new ListView());
root.getChildren().addAll(hiddenSidesPane);
primaryStage.setTitle("HiddenSidesPane Example Demo");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Try the following:
ListView listView = new ListView();
hiddenSidesPane.setLeft(listView);
listView.setOnMouseEntered(e->hiddenSidesPane.setPinnedSide(Side.LEFT)); //Keep left side pinned
listView.setOnMouseExited(e->hiddenSidesPane.setPinnedSide(null)); //unpin when mouse exits

Close event in Java FX when close button is pressed

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

Periodically modify a JavaFX StackedBarChart CategoryAxis

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.

Creating multiple stages in JavaFX

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);
}
}