I'm trying to create a window for an application. The size of the window is determined by getting the bounds of the primary screen. In order to do this I have created two classes.
RealMain class is the actual class that does everything:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class RealMain extends Application {
public static final double minWindowWidth;
public static final double minWindowHeight;
//Get primary screen bounds
static {
System.out.println(Thread.currentThread().getName());
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
minWindowWidth = screenBounds.getWidth() / 2;
minWindowHeight = screenBounds.getHeight() / 1.5;
}
#Override
public void start(Stage primaryStage) throws Exception{
Scene scene = new Scene(new HBox(), minWindowWidth, minWindowHeight);
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
primaryStage.setMinWidth(primaryStage.getWidth());
primaryStage.setMinHeight(primaryStage.getHeight());
}
public static void main(String[] args) {
launch(args);
}
}
Main class is just an entry point into the program. All it does is call the main method of RealMain. Why do I need to use the Main class in the first place (why not just run the main method of RealMain)? It's because of a different problem I had, where I would get "Error: JavaFX runtime components are missing, and are required to run this application." when running the program as a jar on macOS. Apparently setting a regular java class as an entry point will fix this (one place where I found this solution is here), hence I needed to create the Main class.
public class Main {
public static void main(String[] args) {
RealMain.main(args);
}
}
It works fine when I run the main method of the RealMain class. However, if I use the main method of the Main class, I get the following error:
Exception in thread "main" java.lang.ExceptionInInitializerError
at RealMain.<clinit>(RealMain.java:19)
at Main.main(Main.java:4)
Caused by: java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = main
at com.sun.glass.ui.Application.checkEventThread(Application.java:441)
at com.sun.glass.ui.Screen.setEventHandler(Screen.java:369)
at com.sun.javafx.tk.quantum.QuantumToolkit.setScreenConfigurationListener(QuantumToolkit.java:684)
at javafx.stage.Screen.<clinit>(Screen.java:74)
... 2 more
So it seems when running the program from Main class, the main thread is used to execute the static block, which seems to be what causes the error (as to why, I have no idea). Meanwhile when running the program from RealMain class, the JavaFX Application Thread is used for the static block, which works fine. I guess the logical solution would be to somehow force the program to change threads and execute the static block on the JavaFX Application Thread, but I have no idea how to do that.
Could anyone please help me figure this out?
Related
I am using a background Thread to run my loading code and wish to bind the MessageProperty of the Task to a label.
However, when calling updateMessage() the task hangs; the message is never updated and the next line of code does not execute.
This is using JDK 1.10.1. Here is an MCVE:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(10);
Label label = new Label("Message");
root.getChildren().add(label);
primaryStage.setScene(new Scene(root));
Task loadingTask = new LoadingTask();
Thread loadingThread = new Thread(loadingTask);
loadingThread.setDaemon(true);
label.textProperty().bind(loadingTask.messageProperty());
loadingThread.start();
primaryStage.setWidth(200);
primaryStage.setHeight(200);
primaryStage.show();
}
}
class LoadingTask<Void> extends Task {
#Override
protected Object call() throws Exception {
System.out.println("Loading task ...");
updateMessage("Loading task ...");
System.out.println("Message: " + getMessage());
return null;
}
}
The output:
Loading task ...
The second System.out.println() is never executed.
Edit:
I added a simple GUI to my MCVE with a label bound to the MessageProperty. The label does get updated to show "Loading task ..." but the console output remains the same; code after the updateMessage() method is called does not execute.
2nd Edit:
I ran my step debugger, and an IllegalStateException is being thrown from the Task class: "Task must only be used from the FX Application Thread"
I'm not sure what that means as the whole point is to run this task on a different thread...
Your only issue is, that you must not access getMessage() from another thread than the FX UI-Thread. Try Platform.runLater(() -> System.out.println("Message: " + getMessage()));
Would it be possible to make an Application object from javaFX?
public class Constructor
{
public Constructor()
{
Application a = new Application()
{
#Override
public void start(Stage window) throws Exception
{
}
};
a.launch();
}
}
It currently gives me this when I try to run it:
Exception in thread "main" java.lang.RuntimeException: Error: class net.whiteautumn.lightcast.Constructor is not a subclass of javafx.application.Application
This, I presume, is caused by the class Constructor not extending Application.
Any way of getting around this or is that the only way to ever launch JavaFX applications?
EDIT:
I know how to start a javaFX application. What I want to to make an application and have it in an object. Is this possible or no?
Application.launch() is a static method, therefore a.launch() is equivalent to Application.launch(). This means that it will try to launch an application of the class that calls it, which is Constructor class.
The correct way to do it is:
private static class MyApp extends Application
{
#Override
public void start(Stage window) throws Exception
{
}
};
Application.launch(MyApp.class, null);
you should extend Application to initializing JavaFX Toolkit; and override start method to show your form(JavaFx stage). obviously, you need main static method to run application.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CircleBuilder;
import javafx.stage.Stage;
public class Constrcutor extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX Abacus");
Pane root = new Pane();
Circle circle = CircleBuilder.create()
.radius(20)
.centerX(20)
.centerY(20)
.build();
root.getChildren().add(circle);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In a JavaFX application, javafx.application.Application must be subclassed, and the inherited launch() method, although it's public, must be called from within this derived class, otherwise an exception is thrown. The launch() method then uses reflection to instantiate the derived class, making it difficult to set values for the class members without losing them when launching. All that appears totally unusual to me, and I was wondering why starting a JavaFX application is so complicated, if that kind of software design (design pattern?) has a name, or if it's just bad design?
EDIT:
To be more specific, I want to use the observer pattern, so my java application gets notified when a document was loaded, like this:
public class MyDocumentLoader extends Application
{
private ChangeListener<Worker.State> changeListener;
public void setChangeListener(ChangeListener<Worker.State> changeListener)
{
this.changeListener = changeListener;
}
...
public void loadDocument(String url)
{
webEngine.getLoadWorker().stateProperty().addListener(changeListener);
webEngine.load(url);
}
...
}
I need the callback member in several methods, and ideally I can have more than one instances of the class that loads documents, so I can set different ChangeListeners for different URLs.
My guess is that this design was motivated by the (vast) number of Swing applications that were incorrectly written, with the "primary" JFrames being instantiated and shown on the wrong thread (i.e. not on the AWT event dispatch thread). My guess is that so many Swing applications were incorrectly written that they had to defensively code the framework against the incorrect usage, and that they wanted to avoid this scenario with JavaFX.
Forcing (well, almost forcing, there are hack-arounds) an FX Application to start this way makes it much harder to write an application incorrectly in a similar way. The launch method (and the equivalent Oracle JVM startup process if you have an Application subclass without a main method and a call to launch) does quite a bit of boilerplate work: it starts the FX toolkit, instantiates the Application subclass and calls its init() method, then on the FX Application Thread it instantiates the primary Stage and passes it to the Application subclass's start(...) method. This then ensures everything is running on the correct thread.
You should basically consider the start(...) method in a JavaFX application as the replacement for the main(...) method in a "traditional" Java application, with the understanding it is invoked on the FX Application Thread.
My recommendation is that the Application subclass should be as minimal as possible; it should just delegate to something else to actually create the UI, and then should just place it in the primary stage and show it. Include a main method that does nothing other than call launch(...) as a fallback for non-JavaFX-aware JVMs. You should only have one instance of one Application subclass present in any JVM. This way your Application subclass has no class members to set, and so the issues you describe simply don't arise.
If you use FXML, this is actually fairly natural: the start(...) method essentially just delegates to the FXML-controller pair to do the real work. If you don't use FXML, create a separate class to do the actual layout, etc, and delegate to it. See this related question which gets at the same kind of idea.
Note also that your statement
the inherited launch() method, although it's public, must be called
from within this derived class
is not entirely accurate, as there is an overloaded form of the launch(...) method in which you can specify the application subclass. So, if you really need, you can just create a stub for starting the FX toolkit:
public class FXStarter extends Application {
#Override
public void start(Stage primaryStage) {
// no-op
}
}
Now you can do:
public class MyRegularApplication {
public static void main(String[] args) {
// start FX toolkit:
new Thread(() -> Application.launch(FXStarter.class)).start();
// other stuff here...
}
}
Note that launch does not return until the FX toolkit shuts down, so it is imperative to put this call in another thread. This potentially creates race conditions, where you may try to do something needing the FX toolkit before launch(...) has actually initialized it, so you should probably guard against that:
public class FXStarter extends Application {
private static final CountDownLatch latch = new CountDownLatch(1);
public static void awaitFXToolkit() throws InterruptedException {
latch.await();
}
#Override
public void init() {
latch.countDown();
}
#Override
public void start(Stage primaryStage) {
// no-op
}
}
and then
public class MyRegularApplication {
public static void main(String[] args) throws InterruptedException {
// start FX toolkit:
new Thread(() -> Application.launch(FXStarter.class)).start();
FXStarter.awaitFXToolkit();
// other stuff here...
}
}
SSCCE (I just used inner classes for everything so this is convenient to run, but in real life these would be standalone classes):
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BackgroundProcessDrivenApp {
public static void main(String[] args) throws InterruptedException {
Platform.setImplicitExit(false);
new Thread(() -> Application.launch(FXStarter.class)).start();
FXStarter.awaitFXToolkit();
new MockProcessor().doStuff() ;
}
public static class FXStarter extends Application {
private static final CountDownLatch latch = new CountDownLatch(1);
#Override
public void init() {
latch.countDown();
}
public static void awaitFXToolkit() throws InterruptedException {
latch.await();
}
#Override
public void start(Stage primaryStage) { }
}
public static class MockProcessor {
private final int numEvents = 10 ;
public void doStuff() {
Random rng = new Random();
try {
for (int event = 1 ; event <= numEvents; event++) {
// just sleep to mimic waiting for background service...
Thread.sleep(rng.nextInt(5000) + 5000);
String message = "Event " + event + " occurred" ;
Platform.runLater(() -> new Messager(message).showMessageInNewWindow());
}
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
} finally {
Platform.setImplicitExit(true);
}
}
}
public static class Messager {
private final String message ;
public Messager(String message) {
this.message = message ;
}
public void showMessageInNewWindow() {
Stage stage = new Stage();
Label label = new Label(message);
Button button = new Button("OK");
button.setOnAction(e -> stage.hide());
VBox root = new VBox(10, label, button);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 120);
stage.setScene(scene);
stage.setAlwaysOnTop(true);
stage.show();
}
}
}
JavaFX supports a great number of deployment and packaging strategies, ref. https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/toc.html, and having a standardized lifecycle entry- and exit-point simplifies supporting all these strategies.
If you are struggling to initialize your main application class, due to it being instanciated by the JavaFX launcher, your best option is to use the Application.init() and Application.stop() methods, as James_D points out.
I have a created a very simple processing sketch in eclipse with classes.Eclipse says the code contains no errors, only warnings, however it fails at runtime and the console displays the following errors:
Exception in thread "Animation Thread" java.lang.NullPointerException
at processing.core.PApplet.ellipse(PApplet.java:12174)
at assignment.Tracking.display(Tracking.java:15)
at assignment.Assignment.draw(Assignment.java:16)
at processing.core.PApplet.handleDraw(PApplet.java:2386)
at processing.core.PGraphicsJava2D.requestDraw(PGraphicsJava2D.java:240)
at processing.core.PApplet.run(PApplet.java:2256)
at java.lang.Thread.run(Unknown Source)
The code is the following:
package assignment;
import processing.core.*;
public class Assignment extends PApplet {
Tracking tracking;
public void setup() {
size(500, 500);
tracking = new Tracking();
}
public void draw() {
tracking.display();
}
}
And the code for the Tracking class is:
package assignment;
import processing.core.*;
public class Tracking extends PApplet
{
int test;
public Tracking() {
test = 100;
}
void display(){
ellipse(test, test, test, test);
}
}
I believe I have imported the necessary processing .jar files. Thanks in advance for any help
It looks like the problem is that you do the setup in the Assignment class, but calls ellipse() from the Tracking class. Note that Assignment and Tracking are two different PApplets. You call size(500, 500) in Assignment, but Tracking has no defined size.
I would like to create a class called EnhancedStage which adds functionality to Stage.
The problem is, javaFx creates the primary stage and passes it into the application, instead
of allowing me to create it. This way I'm not able to use my own class for the primary stage.
I don't understand why the system creates the Stage, and would I be losing anything by not
using that stage and instead constructing another one ?
Thanks
If you don't want to use the stage which is a parameter of the start() method, then don't call show() on that stage, just create your own stage and only call show() on your custom stage. I suggest doing this inside the start method rather than via a runLater call from init. I don't see any significant drawback in such an approach.
As to why a stage is passed in start, MadProgrammer's guess is as good as any: Simplifies life for programmers, removes some boilerplate code from basic apps and limits app startup coding errors. For all those reasons, for most people, I'd generally just advise using the provided stage rather than creating your own.
Suggested approach if you need or desire to use a stage subclass for your primary stage:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class StageTester extends Application {
#Override public void start(final Stage systemStage) throws Exception {
UserStage userStage = new UserStage();
userStage.show();
}
private class UserStage extends Stage {
FlowPane layout = new FlowPane();
UserStage() {
super();
foo();
setScene(new Scene(layout));
}
public void foo() {
layout.getChildren().add(new Label("foo"));
}
}
public static void main(String[] args) { launch(args); }
}
There is nothing stopping you from creating another stage and ignoring the one you get from the start method. There are number of ways to do this:
Extend Stage and add your additional functionality
public class EStage extends javafx.stage.Stage {
... // Do your magic here
}
or Extend Window
public class EStage extends javafx.stage.Window {
}
In either case, to use your new stage, you can just create it in the init method and use it in the start method, ignoring the stage supplied to you
public class ShowCase extends Application {
private Stage mStage;
#Override
public void init() throws Exception {
Platform.runLater(() -> {
mStage = new EStage();
//mStage.impl_setPrimary(true);
}
}
#Override
public void start(Stage primaryStage) {
mStage.setScene(...)
}
}
The JavaFX architecture is based upon a container which manages the JavaFX Event Loop (on the JavaFX application thread) for your application (which is why the entry class for your application must inherit from javafx.application.Application).
Because JavaFX applications are container managed, you gain a few benefits:
the container can inject JavaFX control references into your controller class instances via the FXML loader
the container manages event propagation from the event loop all the way down to the target control and back up to the event loop again without complex event handling on your part
Of course, the event loop requires a top-level object for event propagation and this top-level object is the Stage object, which gets passed to the start() method of your Application object.