JavaFX issue with static keyword; with Minimal, Complete, and Verifiable example - java

Good day,
When I run this Code:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class mcve extends Application {
static Label myScore = new Label("Test");
static Rectangle rect = new Rectangle(0,0,10,10);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
myScore.setTextFill(Color.WHITE);
myScore.setLayoutY(20);
myScore.setLayoutX(200);
myScore.setFont(new Font("Arial", 30));
myScore.setText("0");
rect.setFill(Color.WHITE);
final Group group = new Group(myScore,rect);
Scene scene = new Scene(group, 500, 500, Color.BLACK);
stage.setScene(scene);
stage.show();
}
}
it creates the following exception:
Exception in thread "main" java.lang.ExceptionInInitializerError
at mcve.<clinit>(mcve.java:11)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:122)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 4 more
However if I remove the static keyword before Label at the top, the code runs just fine.
My question is: Why does the error occur when creating a static Label but not a static Rectangle? I want the Label to be static and not an object of a class.

Why does the error occur when creating a static Label but not a static Rectangle?
Essentially, this is a matter of initialization order. The UI platform needs to be properly initialized when creating Node objects. Potentially, it could also happen with a Rectangle, but most likely a Label (which is a Control) relies even more on a properly initialized platform. In this particular case, the difference is that Shape objects do not require CSS, while Control objects do. This causes the platform methods to be called as seen in the stack trace, at a point when the toolkit is not yet initialized.
The static class member is initialized when class mvce is loaded. This is done before the main() method is called, and hence before the launch() method is called. The platform is not initialized yet at this point.
The non-static member, on the other side, is initialized when class mvce is instantiated. The class mvce is instantiated internally by the launch() method, after the toolkit has been properly initialized.
Also, there is usually no reason to use static references. Just use a member.

Have you tried to just declare the static label, but instanciate and set it later on in the start() method? This should work, too.

Related

Is it possible to define a scene in a separate class?

This may have been asked before but i was not able to find an answer. Im working on a JavaFX app that contains a lot of scenes and a lot of animation. Currently I'm having different Animationtimers and different Scenes all defined inside the start() function, inside the main class that extends Application. However the code gets very messy and long.
Is there a way in which you can define all of these things in a separate Java class, and then simply do something like primaryStage.setScene(MyScene.getScene) - MyScene being the java class that has all your scene code.
Something like this:
public class TestScene {
private Group root = new Group();
Scene test = new Scene(root);
Button button = new Button("test");
root.getChildren.add(button);
}
And actually having that code be a scene that you can just import and set on primaryStage.
Edit: I have no idea why this was so difficult for my mind, as Bertijn said I obviously just need to use a constructer. For whatever reason I forgot that, and so I obviously couldent perform a root.getChildren.add(button), outside a function of some sort.
If anybody else struggles with this here is the super simple solution:
Class containing our scene:
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;
public class OurScene {
public Scene getScene() {
Group root = new Group();
Scene scene = new Scene(root, Color.GREEN);
Button button = new Button("Hello world!");
root.getChildren().add(button);
return scene;
}
}
And then to add it to primaryStage:
import javafx.application.Application;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
OurScene ourScene = new OurScene();
primaryStage.setScene(ourScene.getScene());
primaryStage.show();
}
}
Try making a class called Scenes. In the constructor, created all your scenes, you can give them an id if you want. In your main class, just create an instance of this class Scenes scenes = new Scenes();. The scenes get created. Then you can access them by creating a getScene(String id) method.
Hope I understand your question correctly, and if this doesn't answer it, feel free to get back to me!

Can I use multiple classes to control one FXML file?

I made an application in Scene Builder on one FXML. I am making a server with JavaFX so I can learn JavaFX and get more familiar with Java's networking libraries.
I have a server terminal Tab and additional tabs within a TabPane. I wanted to make classes that extend upon the main controller class to handle components in each tab.
While trying to implement this I found that the FXMLLoader won't be able to read things if the #FXML annotated variables are static. And the #FXML annotated event listener method won't be read if that is static either.
And if I try any kind of workaround I get nullpointerexceptions when trying to change text in the TextArea. I really don't want to have to use multiple FXML files but it's seeming like I'll have to because if I can't make these static then it just won't work.
Within the Server Terminal Tab there is a TextArea, a TextField, and a Button.
Here's my working code:
package me.Cronin.Keith.JavaServer;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class JavaServer extends Application {
#FXML
public Button btnSendCommand;
#FXML
public TextField consoleInputField;
#FXML
public TextArea serverTerminal;
public static FXMLLoader fxmlLoader = new FXMLLoader(JavaServer.class.getResource("JavaServer.fxml"));
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage) throws IOException
{
Parent p = fxmlLoader.load();
Scene scene = new Scene(p);
stage.setTitle("Java Server v1.0");
stage.setScene(scene);
stage.show();
}
#FXML
public void clickSendCommand(MouseEvent event)
{
serverTerminal.setText("I got clicked..");
}
}
Here's the other class that I don't know what to do with yet:
package me.Cronin.Keith.JavaServer.Terminal;
import me.Cronin.Keith.JavaServer.JavaServer;
public class Terminal extends JavaServer {
public static void logTerminal(String msg)
{
}
}
Is there anything I can do to change this to support what I want to do?
I want to be able to control the variables (JavaFX Components from FXML) in JavaServer.class with my other classes that extend upon it.
I have seen this question here:
Multiple controller for one FXML file
But it doesn't answer my question.
It's seeming like I'd have to put everything in the main controller class or have multiple Fxml files.
I was able to make this work by replacing my Terminal class with this:
Terminal.java
package me.Cronin.Keith.JavaServer.Terminal;
import me.Cronin.Keith.JavaServer.JavaServer;
public class Terminal extends JavaServer {
public static void logTerminal(String msg)
{
JavaServer mainController = fxmlLoader.getController();
mainController.serverTerminal.setText(msg + "\n");
}
}
So the answer is YES you can.
In JavaServer.java I just called Terminal.class statically and then used the static method logTerminal() within it which worked. I extended JavaServer.java in Terminal.java so I could statically call the controller's FXMLLoader.

Dynamically adding checkboxes in JavaFX

I'm trying to create a game prediction system in Java with GUI based on JavaFX using scene builder. It contains a HashMap containing a game object in which there is an ArrayList containing the participating athletes. Both of which will be decided by the user at runtime. Only a certain type of athletes will be eligible for participating in a certain type for a game, for example, swimmers will be able to participate only in swimming games. I can get the eligible athletes at by matching their types with game object and athlete object, but how do I create the checkboxes(for the eligible athletes at runtime?
You can use following approach to add or remove checkboxes at runtime
package com.grsdev.stackoverflow.question170919.pack02;
import javafx.application.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* #author gaurav salvi
*
*/
public class JavaFxCheckBoxDemo extends Application{
private static VBox root;
private static CheckBox box=new CheckBox("Apple");
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
root=new VBox();
Button button=new Button(" toggle ");
button.setOnAction(JavaFxCheckBoxDemo::buttonClicked);
root.getChildren().add(button);
root.getChildren().add(box);
Scene value=new Scene(root,200,200);
stage.setScene(value);
stage.show();
}
private static void buttonClicked(ActionEvent event){
if(root.getChildren().contains(box)){
root.getChildren().remove(box);
}else{
root.getChildren().add(box);
}
}
}
I think, dynamically creating objects in Scene Builder and linking them in the controller on basis of their ID would very difficult if not impossible. One possible workaround for the given problem would be adding several static objects and set their visibility to hidden. As and how athletes were created and getting added to the ArrayList in the Game object I changed the visibility for the given checkbox.

Javafx Donut Chart in FXML

I want to do a Doughnut/Donut chart on JavaFX and searching I came to this example: Can PieChart from JavaFX be displayed as a doughnut?
I Works really nice, but since I'm using FXML to make my GUI, I can't use this example. First, I tried to add the DoughtnutChart.java class as a #FXML var in the controller class of the panel where I want to insert it, but launched errors.
Then, searched in Google to make the DoughnutChart a custom component, but all the examples are based on Panes. Also, If I try to import my donu.jar to SceneBuilder, the window to select a component is empty.
So, my question is: How do I implement this Doughnut Chart on JavaFX when my GUI is made on FXML?
Thanks a lot.
It's hard to tell what the cause of your error is without seeing the FXML and the error message.
I got this to work pretty easily: the one thing to be aware of is that the FXMLLoader instantiates classes by invoking the no-argument constructor. If it can't find one, it tries to use a builder class as a back-up plan. So the one modification you need to make to #jewelsea's DoughnutChart implementation is to add a no-argument constructor. (You could also define a DoughnutClassBuilder, but that's a lot more work, and doesn't get you any extra benefit.) So I did this:
package doughnut ;
// imports as before...
public class DoughnutChart extends PieChart {
private final Circle innerCircle;
public DoughnutChart() {
this(FXCollections.observableArrayList());
}
// everything else as before...
}
Then the following FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import doughnut.DoughnutChart?>
<StackPane xmlns:fx="http://javafx.com/fxml" fx:controller="doughnut.SampleController">
<DoughnutChart fx:id="doughnutChart" />
</StackPane>
with the controller SampleController.java:
package doughnut;
import javafx.fxml.FXML;
import javafx.scene.chart.PieChart;
public class SampleController {
#FXML
private PieChart doughnutChart ;
public void initialize() {
doughnutChart.getData().addAll(
new PieChart.Data("Grapefruit", 13),
new PieChart.Data("Oranges", 25),
new PieChart.Data("Plums", 10),
new PieChart.Data("Pears", 22),
new PieChart.Data("Apples", 30));
}
}
and the application class
package doughnut;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = (StackPane)FXMLLoader.load(getClass().getResource("DoughnutChartDemo.fxml"));
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
work exactly as expected.
I had to replace this with super in James_D answer to make it work.
For me the constructor looked like:
public DoughnutChart()
{
super(FXCollections.observableArrayList());
innerCircle = new Circle();
// just styled in code for demo purposes,
// use a style class instead to style via css.
innerCircle.setFill(Color.WHITESMOKE);
innerCircle.setStroke(Color.WHITE);
innerCircle.setStrokeWidth(3);
}

How to use javafx.beans.binding.Bindings.select(...) for concise value binding

Overview
As a Swing developer of ten years, I've been thrilled with the features introduced with JavaFX 2.0, especially the rich, fluent, high-level data-binding facilities. This facility alone is worth the cost of learning a new API (which is much less since abandoning FX script). It's going to have a direct impact on the readability and maintainably of my model/view synchronization code.
So far I'm having great success at first level and basic derived bindings, but am struggling to figure out the "JavaFX way" of binding one value to a value two or more levels of indirection in the data graph.
Problem
As shown in the code example below, I'm attempting to use javafx.beans.binding.Bindings.select() to synchronize the text value of a Label with one of the contained properties of the currently selected item in a ComboBox. This code is a simple example of something more complex I'm trying to do, so I understand that it's not hard to do this with the lower level bindings API. I'd like to know if it's possible with the higher-level fluent API, and if the select(...) method actually tracks changes in the indirect properties (i.e. update property if either the direct property or the selected subproperty change).
The documentation and examples on select(...) are sparse, so I'm hoping someone with advanced experience with this can tell me if I'm trying to use the API as designed, or if there's another way to use the high-level binding API to do what I want.
Sample Code
Here's the demo code. When run, there's a ComboBox with two items in it, and then two labels. The first label shows the toString() version of the selected item. The second label attempts to display one of the properties of the selected item, but only displays null.
import static javafx.beans.binding.Bindings.*;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/** Testing cascading binding change triggers. */
public class SandboxTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox(8);
root.setStyle("-fx-padding: 8;");
Scene s = new Scene(root);
stage.setWidth(200);
stage.setScene(s);
ComboBox<MoPoJo> list = new ComboBox<SandboxTest.MoPoJo>();
list.itemsProperty().set(FXCollections.observableArrayList(new MoPoJo("foo", "bar"), new MoPoJo("baz", "bat")));
Label direct = new Label();
direct.setTooltip(new Tooltip("Selected item to string"));
Label withSelect = new Label();
withSelect.setTooltip(new Tooltip("Second property of selected item"));
direct.textProperty().bind(convert(list.getSelectionModel().selectedItemProperty()));
withSelect.textProperty().bind(convert(select(list.getSelectionModel().selectedItemProperty(), "two")));
root.getChildren().addAll(list, direct, withSelect);
stage.show();
}
private static class MoPoJo {
private StringProperty _one = new SimpleStringProperty();
private StringProperty _two = new SimpleStringProperty();
private StringProperty _name = new SimpleStringProperty();
public MoPoJo(String o, String t) {
_one.set(o);
_two.set(t);
_name.bind(format("{ %s, %s }", oneProperty(), twoProperty()));
}
public StringProperty oneProperty() {
return _one;
}
public StringProperty twoProperty() {
return _two;
}
public ReadOnlyStringProperty nameProperty() {
return _name;
}
#Override
public String toString() {
return nameProperty().get();
}
}
}
Bindings.select can't access private class. Make MoPoJo a public class and your code will work.
public static class MoPoJo {
P.S: I believe that fact worth to be mentioned in docs, so I filed http://javafx-jira.kenai.com/browse/RT-20640 on JavaFX javadoc.

Categories