Javafx Donut Chart in FXML - java

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

Related

Distinguish between foo(int) and foo(Integer) in java

I haven't been able to find this, though I currently have a usecase where I have a generic type which has a method foo(int) and a method foo(T).
For my usecase said type is instantiated with T = Integer, meaning I have the methods foo(int) and foo(Integer).
Whenever I try to call foo(Integer) it calls foo(int) instead, no matter whether the type is specified, whether I cast or not. The only thing solving it is using a Long instead, which I do not want to do.
Is there any way I can force java to use the foo(Integer) method?
Edit:
For once, to answer the comment, I don't think code was relevant here, as what I described was formable enough to understand what I meant.
Secondly, the error was on my end, I apologise. I didn't have the expected behaviour and thought it'd be because of an issue in that regard, especially since my IDE displayed the usage of the foo(int) method. I'll be closing this now
A MVCE:
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(Main,args);
}
}
Controller.java
package sample;
import javafx.collections.FXCollections;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
public class Controller implements Initializable {
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
ListView<Integer> listView = new ListView<>();
listView.setItems(FXCollections.observableArrayList(1, 5, 8, 13));
Integer t = 5;
listView.getSelectionModel().select(t);
System.out.println(listView.getSelectionModel().getSelectedItems());
}
}
sample.fxml
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>
You will notice this code works as expected, what I figured out now though is that, since I'm not using java but rather groovy - switching the file endings to groovy and compiling with a groovy compiler makes this program have my described behaviour, which means the issue is groovy related not java related.
You question as stated has a simple answer:
class Foo<T> {
void foo(int i) {
System.out.println("foo(int)");
}
void foo(T t) {
System.out.println("foo(T)");
}
}
private void test() {
Foo<Integer> foo = new Foo<>();
foo.foo(1);
foo.foo((Integer)1);
foo.foo(Integer.valueOf("1"));
}
prints:
foo(int)
foo(T)
foo(T)
However, I suspect you've tried this so please post some example code.
If you like, check out the rules for method selection here: https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.3.

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!

JavaFX Preloader issue

Hello dear Stackoverflow,
I have several questions regarding the JavaFX class Preloader.
After a long-term research, I haven't found any proper solution to my problem and I think it's not really the issue with my code, but rather the limits of JavaFX itself when it comes to the Preloader class.
Let me get straight up to my point: Is it not possible to use FXML to define the design of a Preloader? It's interesting to see how all the tutorials only guide you throughout creating a preloader by creating new Instances out of the classes. (ProgressBar progressBar = new ProgressBar(); ie)
So, let's take this example code for you to test this with me:
TestPreloader:
import com.sun.javafx.application.LauncherImpl;
import javafx.application.Preloader;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TestPreloader extends Preloader {
#FXML
ProgressBar progressBar;
public static void main(String[] args) {
LauncherImpl.launchApplication(TestApplication.class, TestPreloader.class, args);
}
public void initialize() {
System.out.println("initialize printed");
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.initStyle(StageStyle.TRANSPARENT);
Parent root = FXMLLoader.load(getClass().getResource("preloader.fxml"));
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.centerOnScreen();
primaryStage.show();
}
#Override
public void handleProgressNotification(ProgressNotification progressNotification) {
// progressBar.setProgress(progressNotification.getProgress());
}
#Override
public void handleStateChangeNotification(StateChangeNotification notification) {
System.out.println(notification.getType().toString());
switch (notification.getType()) {
case BEFORE_START:
//progressBar.setProgress(1);
break;
case BEFORE_LOAD: //this is where the TestApplication class will be constructed
//progressBar.setProgress(1);
break;
case BEFORE_INIT:
//progressBar.setProgress(1);
break;
}
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="TestPreloader">
<children>
<ProgressBar fx:id="progressBar" layoutX="174.0" layoutY="141.0" prefWidth="200.0" progress="0.0" />
</children>
</AnchorPane>
TestApplication
import javafx.application.Application;
import javafx.stage.Stage;
public class TestApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
System.out.println("Application started!");
}
}
The output if you start is in the following order:
initialize printed
BEFORE_LOAD
BEFORE_INIT
BEFORE_START
Application started!
now, as you can see the initialize() method is being called firsthand, then the state of the preloader changes and thus upon every change the notification pops up - all good until now.
Yet what bothers me is, if I call progressBar#setProgress IN the initialize method, the progress actually gets set to 1 and no errors are being thrown. Although the States occur AFTER the Preloader is initialized, NullPointers occur.
To understand what I mean, uncomment setProgress under handleStateNotification (under the BEFORE_LOAD statement would make the most sense to me), and you'll see that a NullPointerException pops up.
Now, why does it occur? What can I do to fix this issue? It just doesn't make any sense for me. If the Preloader is initialized and the ProgresssBar is not null, how can it turn null afterwards? Perhaps maybe there is a new instance of the own class being used separately? But that wouldn't make sense as well to me. Why would you have that?
I'm stuck on this problem for a few days now, I mean I could just go for a Timeline and "hardcode" it, but well, I want to learn what causes these issues.
TLDR;
ProgressBar turns null after initializing the application when using the Preloader class, what causes it and what should I do?
EDIT:
It seems like the class is not being reconstructed somewhere, idk i'm really stuck lol
When the FXMLLoader loads your FXML file, it creates a new instance of the specified controller. However, this is not the desired outcome. The object that receives your state callbacks should be the controller of your FXML window.
Replacing your FXMLLoader.load(...) block with
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("preloader.fxml"));
fxmlLoader.setController(this);
Parent root = fxmlLoader.load();
and removing the controller property from your FXML, causes the FXMLLoader to use the given controller instance (being the one that receives the state callbacks) instead of it constructing a new instance of the specified controller class.

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.

How can I combine a FXML file and a group in JavaFX?

So I made a FXML file for a little game I'm making for school and it has some buttons and labels in it, and it has it's own controller. Now I made a group of rectangles and want to add it to the same scene as the fxml file.
button.getParent().getChildren().add(group);
The code I wrote here doesn't work. Anybody an idea on how to add the group in the fxml file or just render it on the scene?
Rendering the fxml and the group in 2 diffrent scenes does work, so there are no errors.
EDIT:
Application class:
package retris;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author Arno Vandersmissen, Casper Vranken, Rani Vanhoudt
*/
public class Retris extends Application {
private Stage stage;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLRetris.fxml"));
Parent root = loader.load();
FXMLRetrisController controller = loader.getController();
controller.playMusic();
stage.setOnCloseRequest(e -> {
e.consume();
FXMLConfirmController confirm= new FXMLConfirmController();
if(confirm.close("Close?")){
Platform.exit();
}
});
Scene scene = new Scene(root);
stage.setTitle("Retris");
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
A Scene can only display ONE Parent at a time. Whatever you want to display in your GUI would be contained in that Parent. Assuming, as you suggested in the comments, that you want to update that parent at runtime, you need to have a reference to whatever child of the parent that should contain your group of rectangles.
let's say the root element of your fxml file is AnchorPane, and you also want to add the group of rectangles to that root. In your .fxml file you need a fx:id tag <AnchorPane fx:id="myRoot"> this allows you to inject the element to your controller class by use of the #FXML annotation.
public class MyController {
#FXML private AnchorPane myRoot;
#FXML private void createAndAddRectangles {
/**myRoot is already instantiated. you can simply add nodes to it at runtime
by using onAction="createAndAddRectangles" tag on a button in your .fxml file.**/
}
}

Categories