I'm working on a game in JavaFX, and right now I'm trying to create a loading screen, since loading the assets takes some time. I've created a LoadingPane class that displays several progress bars, and I know for sure that it works. However, in the below code, the loading pane will not be visible until after the loadAssets function, even though I'm adding it beforehand. When I run the below code, I get a blank stage for the time it takes for the assets to load, and then a screen with the completed progress bars.
I haven't been able to find anyone with similar issues, or any sort of refresh or update function to force the scene to display the loading pane before continuing with the program.
Note: I've deleted some irrelevant code setting up keyboard input handling.
public class Main extends Application{
Pane root = new Pane();
Scene mainScene = new Scene(root, Constants.WINDOW_WIDTH, Constants.WINDOW_HEIGHT, Color.BLACK);
static LoadingPane loadingPane = new LoadingPane(3);
private static int loadingIndex = 0;
public static void main(String[] args) {
if(Constants.DEBUG_MODE)
System.out.println("WARNING: Game has launched in debug mode!");
launch(args);
}
public static void updateProgress(double percent){
loadingPane.setBarLength(loadingIndex, percent);
}
public static void loadAssets(){
RoomLoader.createRooms();
updateProgress(1.0);
loadingIndex++;
ProjectileLoader.load("imgs/projectiles/");
ProjectileLoader.load(Constants.BATTLE_IMAGES_FILEPATH);
updateProgress(1.0);
loadingIndex++;
BattleLoader.createBattles();
updateProgress(1.0);
loadingIndex++;
}
public static void updateProgress(double percent){
loadingPane.setBarLength(loadingIndex, percent);
}
#Override
public void start(final Stage primaryStage) {
//root.getChildren().add(new javafx.scene.image.ImageView(new Image("imgs/loading.png")));
root.setLayoutX(0);
primaryStage.setScene(mainScene);
primaryStage.show();
primaryStage.toFront();
primaryStage.setTitle("Branch");
primaryStage.setResizable(false);
//primaryStage.getIcons().add(new Image("core/imgs/soul/red.png"));
//This allows the closing of the primaryStage to end the program
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent t) {
System.exit(0);
}
});
root.resize(Constants.WINDOW_WIDTH, Constants.WINDOW_HEIGHT);
primaryStage.getIcons().add(new Image("imgs/icon.png"));
//End GUI setup
//The problem lines
root.getChildren().add(loadingPane);
//refresh root?
loadAssets();
}
}
EDIT: Working Code
For anyone who arrives here with a similar issue, below is the code I used to get this to work:
I replaced this:
//The problem lines
root.getChildren().add(loadingPane);
//refresh root?
loadAssets();
With this:
root.getChildren().add (loadingPane);
Task<Integer> loadingTask = new Task<Integer>() {
#Override protected Integer call() throws Exception {
loadAssets();
return 1;
}
};
loadingTask.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
#Override
public void handle(WorkerStateEvent t){
loadingPane.setVisible(false);
load(); //note: this function sets up the actual game
//updating the GUI, adding game elements, etc
}
});
new Thread(loadingTask).start();
I can't say that this is the best way to go about this, but I can say that it works. Good luck!
You need a separate thread for the update method.
Code runs in a linear fashion, one bit of code runs then the next. With a separate thread, the two “lines” of code can run side by side. The process runs and the GUI updates at the same time.
JavaFX application runs on specific thread called Application Thread, in other to make GUI responsive while doing some expensive operation, like in your case loading assets, you will need to load assets on another Thread that you create yourself or you can use Task which is one of JavaFX classes meant to be used in such use cases.
I suggest you to read about Task in official javadocs
Related
I am following on from a previous question: link
I am writing a standalone visualization package for a simulation package
The simulation package is written by one of our team in Scala
What I want to do, is create a matplotlib like package for this simulation package. My envisioned end use would look something like matplotlib:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.show()
plt.plot([10, 20, 3000, 4121212])
plt.ylabel('some numbers')
plt.show()
For my package, I would do something analogous. Assume here that myFXPackage is a chart package written in ScalaFX. In my DRIVER class:
Import myFXPackage
// run some simulation code here...
myFXPackage.plot(results)
// run some MORE simulation code here...
myFXPackage.plot(results)
Now it seems that for ScalaFX, there can only be one entry point for the whole app; this is the JFXApp class. However, I want to import the package and simply run this code multiple times in my DRIVER class as shown above. So somehow how DRIVER class would call ScalaFX and run the plot, close it, then run another plot.
Is this feasible? If so how would I go about doing this?
Most JavaFX example code conflates the main method with the Application subclass, and in many cases even does the UI layout, etc., in the same class. There's not necessarily a reason to do this, and in the latter case it's not a particularly good design.
Assuming you separate concerns appropriately in your UI code, you might have a class like
public class MainUI {
private BorderPane root ;
public MainUI() {
root = new BorderPane();
// do layout, register event handlers, etc etc
}
public Pane getView() {
return root ;
}
}
Then if you want a standalone JavaFX application, you would do
public class JavaFxApp extends Application {
#Override
public void start(Stage stage) {
MainUI ui = new MainUI();
Scene scene = new Scene(ui.getView());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
But this isn't the only place you can use the UI class. In JavaFX (as in most UI toolkits), you must create a new stage, and perform other UI tasks, on the UI thread (i.e. the FX Application Thread).
Additionally, in JavaFX you must explicitly start up the FX toolkit. In the code above, this is done by the Application.launch() method.
JavaFX 9 introduced a Platform.startup(Runnable), which starts the FX toolkit and executes the supplied Runnable on the FX Application Thread.
So, using JavaFX 9 and later, you can have code like
public class SomeApp {
public static void main(String[] args) {
// start FX toolkit
// Since we don't want it to exit when we close a window, set
// implicit exit to false:
Platform.startup(() -> Platform.setImplicitExit(false));
// do some other stuff...
// whenever you need to:
Platform.runLater(SomeApp::showStage);
// do other stuff...
}
private static void showStage() {
Scene scene = new Scene(new MainUI().getView());
Stage stage = new Stage();
stage.show();
}
}
Prior to JavaFX 9, you can still do this, but you need to launch a "blank" application:
public class FXStartup extends Application {
#Override
public void start(Stage ignored) {
// probably need this:
Platform.setImplicitExit(false);
}
}
Note that the launch() method blocks until the toolkit exits, so your application needs to start it in another thread:
public class SomeApp {
public static void main(String[] args) {
// start FX toolkit
new Thread(() -> Application.launch(FXStartup.class, args)).start();
// do some other stuff...
// whenever you need to:
Platform.runLater(SomeApp::showStage);
// do other stuff...
}
private static void showStage() {
Scene scene = new Scene(new MainUI().getView());
Stage stage = new Stage();
stage.show();
}
}
I've been having troubles generating a second stage/scene in my program. I have a first stage/scene that generates at launch, works perfectly with a working button. It is in it's own class and executes at the beginning.
Further along in the program I have a method that I was hoping to launch my next stage from. I had created another class below for the generation of my second stage, and placed the constructor within this method. When the method is executed, a stage is being created, but the scene isn't showing anything, just the stage with the proper title. I've tried adding in simple buttons, labels with text, but nothing shows. Not really sure what could be the cause, but after fiddling for awhile I decided to request some help from you guys. Given my limited knowledge of JavaFX, I was wondering if the first stage is causing problems for the second one? I leave it open and running, should I close it before this second executes? Or is there a better way to create a second stage without creating a whole new class? Is the construction of the secondScene class a possible problem?
Let me know, thanks!
EDIT: I have now added a runtime exception handler in, and it does trigger a runtime exception when this class is created. I'm still a bit new to Java, does this mean the problem lies within the class I've created? Or can it still be something earlier on in the code? I have still not been able to get the simple class to create inside this program effectively, so I'm unable to produce a minimum level of functionality here, but according to other people, the code does work when it stands alone.
public SecondScene() {
Thread t = new Thread(new adminThread());
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t + " throws exception: " + e);
}
});
// this will call run() function
t.start();
Text textOutput = new Text(textToDisplay);
BorderPane borderPane = new BorderPane(textOutput);
Scene secondScene = new Scene(borderPane, 400, 600);
// New window (Stage)
Stage secondWindow = new Stage();
secondWindow.setTitle("Second Window");
secondWindow.setScene(secondScene);
secondWindow.show();
}
public void setRunningText(String text){
this.textToDisplay = text;
}
}
class adminThread implements Runnable {
public void run() {
throw new RuntimeException();
}
}
I'm trying to achieve a feast which many attempted here on StackOverflow: showing the console output of a Java application in a TextArea built on JavaFX.
I've managed to show my output in said TextArea but, as many others, my UI freezes, 'cause this thread is heavily loading the one used to show the UI itself.
So I've started reading about Platform.runLater(), but it doesn't solve my issue, mostly because I'm outputting a lot of text and this slows down said function. Looking around, I've got into this question, where a nice solution based on Task is proposed. Neverthless, my UI keeps freezing as soon as I start to show my console log into the TextArea. I'll show you a snippet of my code, so that you may be able to tell me what I'm missing and/or doing wrong.
This is a snippet of my JavaFX controller:
public class MainViewController extends AbstractController implements Initializable {
#FXML private TextArea textAreaLog;
#Override
public void initialize(URL location, ResourceBundle resources) {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
Console console = new Console(textAreaLog);
PrintStream ps = new PrintStream(console, true);
System.setOut(ps);
System.setErr(ps);
return null;
}
#Override
protected void succeeded() {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
super.succeeded();
textAreaLog.textProperty().unbind();
}
};
textAreaLog.textProperty().bind(task.messageProperty());
new Thread(task).start();
}
// Console Class
public static class Console extends OutputStream {
private TextArea output;
Console(TextArea ta) {
this.output = ta;
}
#Override
public void write(int i) throws IOException {
output.appendText(String.valueOf((char) i));
}
}
}
I've edited the code taken from answer to the question I've previously linked, leaving all the debug messages just to help me out.
That's all. My UI just freezes, even if I'm apparently running my heavy-load task in the background instead of doing that directly in my UI thread.
I think root of the problem is one of the below;
System.out.println("text") is being a synchronized method.
accesing ui component outside of Ui thread
When you call System.out.println("text") from ui thread, the synchronization on System.out will cause UI to freeze for duration of synchronization.
You can check if this is the cause like below;(You have to wrap all your System.out calls like below, for only to test if the above theory is correct)
This will cause println methods to synchronize in different thread.(common-pool threads)
CompletableFuture.runAsync(()->System.out.println("text"));
You should also update output component in ui thread.(Problem is solved with this in this case)
// or create new runnable if you are not using java8
Platform.runLater(()->output.appendText(String.valueOf((char) i)));
Alright, so hello everyone hope you're having a nice day.
So I'm pretty new to Java, however I know most of the basics, so not a superb noob at least; and I'm developing a JavaFX sample application, just for testing.
So I ran into a problem, I have a Main class in which I've created the interface, pretty simple, just a scene with 1 button.
Code for Main:
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Hi I'm a title");
//Initialize the Button Object
Button button = new Button();
button.setText("Bite me");
//Call the Handlers Class, with the name handlers
Handlers handlers = new Handlers();
/*
Here would go the code to pass the 'button' object to Handlers class
So then I can do whatever I need in there
*/
//The Button event, managed by the 'handle' method in Handlers class, that's why the 'handlers' in the
//parentheses
button.setOnAction(handlers);
//Just the scenery, not relevant
StackPane pane = new StackPane();
pane.getChildren().add(button);
Scene scene = new Scene(pane, 300,250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Code for Handlers:
public class Handlers implements EventHandler<ActionEvent> {
//Very sad try of a constructor, so as to pass the 'button' object from Main class
//In here, however I have no idea as to how to pass an Object type and how to receive it
public void Handlers(){
}
//The handle method to manage the 'button.setOnAction' event
//This is where I need the 'button' object to compare the source of the event
//to that specific button, so as to prevent that every single button does the same thing
//'button' object from Main class should go ".equals(button)"
#Override
public void handle(ActionEvent event) {
Main main = new Main();
if (event.getSource().equals()){
}
}
}
I'm adding an event to that button, so I've created a Handlers class to handle (dah) all the events on the Main class, the problem rises it's ugly head when I try to pass the 'button' object from Main to Handlers so then I can get it's source, so that every button doesn't do the same thing.
My question is: How can I pass the 'button' object from Main to Handlers? I know that I can use the Constructor, the only problem with that is that I still quite don't grasp the functionality of the Constructor, nor the correct use of the arguments.
I've read these forums for about 1 hour looking for the solution, and I'm pretty sure I've already encountered it, but due to my ignorance, can't understand it.
The clearest example of this would be:
Similar problem
Sorry for the utterly long post, but... I need help :(
Have a nice day :D
There are a couple of things wrong here that I recommend you learn a little more about before trying to fully tackle your application idea. But I can answer your question and give you some links to get your started.
Intro
here is a post about main(), typically you only have one for an application.
Part I
Look into class inheritance. In a JavaFX application, your main class will typically extend Application and your main() method typically calls Application.launch(), made available to your main class because of access control. Under the hood, launch() is calling Application.start(). Because you're inheriting all of the things from Application, you are required to implement (what is probably, but don't quote me on this) the abstract method start() to do all your UI work, see overriding methods and the related overloading. So, in start(), which is pretty much called in every JavaFX application, you then create a button Handler that can be assigned to multiple Buttons on your Stage.
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Hi I'm a title");
EventHandler eventHandler = new BiteHandler("You've been bitten");
Button biteMeButton = new Button();
biteMeButton.setText("Bite me");
Button biteMeAgainButton = new Button();
biteMeAgainButton.setText("Bite me again");
biteMeButton.setOnAction(eventHandler)
biteMeAgainButton.setOnAction(eventHandler);
StackPane pane = new StackPane();
pane.getChildren().add(biteMeButton);
pane.getChildren().add(biteMeAgainButton);
Scene scene = new Scene(pane, 300,250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Part II
Here is the BiteHandler class. With this construct, check out polymorphism. You could have a BiteHandler, ChewHandler, and LickHandler that can all be stored in a List<EventHandler> due to polymorphism.
With the way EventHandler is setup, essentially the handle() method is what is called when the EventHandler is invoked, a la a button's on click action. So we need to give this thing something to do when the handler is invoked. Since I have no idea how to animate a mouth, I'm just gonna have this thing print to the passed message to the console. Here, I've left a comment about what a constructor is.
public class BiteHandler implements EventHandler<ActionEvent> {
private String message_;
/**
* Think of a constructor as any other method, however, it is special
* in that it is named after the class. Because it is named
* after the class, the compiler knows that the first thing
* that needs to be done when instantiating this class. A
* constructor is "automatic work" that "constructs" your object.
* In this class, when you instantiate it, a message is required.
* this message is stored in a "private class member" to be accessed
* later on when the bite() method is called
*/
public BiteHandler(String message) {
message_ = message;
}
#Override
public void handle(ActionEvent event) {
bite();
}
public void bite() {
System.out.println(message_);
}
}
Finale
On your application, you will have 2 buttons. When you click either button, the phrase "You've been bitten" will print to the console. This is because each button has the same BiteHandler. Pretty fk'ing lack luster for all that work but you've got plenty of homework to do to fully digest this answer
I'm having problem to close my javaFX application, when I click the close button from my stage, my application disappears but if I look for it in my task manager my application still there without close.
I've tried to use this code below to force it close the main thread and all childrens threads but the problem persists.
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
Platform.exit();
}
});
Does your application spawn any child threads? If so have you ensured that you terminate them (assuming that they're not daemon threads)?
If your application spawns non-daemon threads then they (and therefore your app) will continue to live on until such time you kill the process
The only way was to call System.exit(0);
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
Platform.exit();
System.exit(0);
}
});
[EDITED]
System.exit will just hide your application, if you open SO's manager task your application will be there. The correct way is to check your Threads, one by one and close all before close application.
First Look Here
public void start(Stage stage) {
Platform.setImplicitExit(true);
stage.setOnCloseRequest((ae) -> {
Platform.exit();
System.exit(0);
});
}
I currently had this problem while using an ThreadExecutor in the controller.
Application does not exit if the ThreadExecutor is not shutdown.
See here:
how-to-shut-down-all-executors-when-quitting-an-application
As it can be a problem to recognize an application exit in the controller, you can get a reference to the controller from your Application class like so (using the sample application from Eclipse):
public class Main extends Application {
private SampleController controller;
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
BorderPane root = (BorderPane)loader.load(getClass().getResource("Sample.fxml").openStream());
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
controller = loader.<SampleController>getController();
}
catch(Exception e)
{
e.printStackTrace();
}
}
Your Application overrides the stop method, where you can call a housekeeping method of the controller (i use a method called startHousekeeping):
/**
* This method is called when the application should stop,
* and provides a convenient place to prepare for application exit and destroy resources.
*/
#Override
public void stop() throws Exception
{
super.stop();
if(controller != null)
{
controller.startHousekeeping();
}
Platform.exit();
System.exit(0);
}
I was able to fix this problem by calling com.sun.javafx.application.tkExit(). You can read more in my other answer here: https://stackoverflow.com/a/22997736/1768232 (these two questions really are duplicates).
Just a note:
Try checking if you use
Platform.setImplicitExit(false);
Had a similar problem and overflowing my tasks. The above line will not make the stage close, it will hide it.
To imitate pressing 'x' one can do:
stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST))