I wrote a little Clock in JavaFX8. On my GUI, I have 2 Buttons. One button should start the timer and the other button should pause the timer. But whenever I press start, nothing is happening. When I delete the whole if() clause, I am able to start the timer via thread.start and it's updating my GUI. I think my call if(isRunning) isn't working how I want.
I appreciate any kind of help!
My Window.java (Main Application)
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package gui;
import java.util.Observable;
import java.util.Observer;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import logic.Clock;
import logic.ClockObserver;
/**
*
* #author
*/
public class Window extends Application implements Observer {
private Button b_start = new Button("Start");
private Button b_stop = new Button("Stop");
private Label l_time = new Label("gdfgdf");
private HBox buttonbox = new HBox();
private Clock clock = new Clock();
private Thread thread = new Thread(clock);
#Override
public void start(Stage primaryStage) throws Exception {
thread.start();
buttonbox.setSpacing(5.0);
clock.addObserver(this);
buttonbox.setAlignment(Pos.CENTER);
buttonbox.getChildren().addAll(b_start, b_stop);
BorderPane bp = new BorderPane();
bp.setPadding(new Insets(10.0));
bp.setCenter(l_time);
bp.setBottom(buttonbox);
Scene scene = new Scene(bp);
primaryStage.setMinHeight(150);
primaryStage.setMinWidth(250);
primaryStage.setScene(scene);
primaryStage.setTitle("Uhr");
primaryStage.show();
b_start.setOnAction((ActionEvent e) ->{
clock.setRunning(true);
});
b_stop.setOnAction((ActionEvent e) ->{
clock.setRunning(false);
});
}
public static void main(String args[]) {
launch(args);
}
#Override
public void update(Observable o, Object o1) {
Platform.runLater(new Runnable(){
#Override
public void run() {
l_time.setText(clock.getZeit());
}
});
}
}
My Clock.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package logic;
import gui.Window;
import java.util.Observable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
/**
*
* #author
*/
public class Clock extends Observable implements Runnable {
private String zeit = "";
private int sek;
private boolean isRunning = false;
public Clock() {
}
public void setZeit() {
zeit = "" + sek;
}
public String getZeit() {
return zeit;
}
public void setRunning(boolean running){
this.isRunning = running;
}
public boolean isRunning(){
return isRunning;
}
public int getSek() {
return sek;
}
#Override
public void run() {
while (true) {
if (isRunning()) {
try {
sek++;
setZeit();
System.out.println(zeit);
this.setChanged();
this.notifyObservers();
Thread.sleep(1000);
} catch (InterruptedException ex) {
//TODO
}
}
}
}
}
Thanks in advance!
Related
So I've got a method called 'popup' in a javaFX controller class which opens a small popup window on top of the actual application window. This method runs without problem if it's assigned to a button in fxml and the button is clicked, but this is not the way I want to use it.
I've got an other class called 'Timer' with a new task (new thread) which is counting down from a certain number, and at a point it will open a popup window with a message. My purpose is to call and run the 'popup' method from this 'Timer' class. When I call the 'popup' method from here, it starts executing, but the popup window doesn't appear at all. (The method call happens as I get the message "in popup" on console from 'popup' method. )
So why does it work when a button click calls 'popup' method from the fxml file and why not when I call it from an other class? Thanks.
Please see the controller class with 'popup' method and the Timer class below (using Gradle in project):
"SceneController" controller class:
package GradleFX;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
//import java.awt.event.ActionEvent;
public class SceneController implements Initializable {
public static String password = "";
protected static int timercount = 20;
#FXML
private Label PWLabel;
#FXML
private Label bottomLabel;
#FXML
private PasswordField PWField;
#FXML
private Label showPWLabel;
protected static Label myBottomLabel;
private static PasswordField myPWField;
private static Label myShowPWLabel;
private static int tries;
#Override
public void initialize(URL location, ResourceBundle resources) {
Timer timerTask = new Timer();
myBottomLabel = bottomLabel;
myPWField = PWField;
myShowPWLabel = showPWLabel;
new Thread(timerTask).start();
}
**/***********************************************************************
/*This method runs if button is pressed in main application,
but can't make it work by calling it from Timer Class */
public void popup() {
System.out.println("in popup");
Stage dialogStage = new Stage();
dialogStage.initModality(Modality.WINDOW_MODAL);
VBox vbox = new VBox(new Text("Hi"), new Button("Ok."));
vbox.setAlignment(Pos.CENTER);
vbox.setPadding(new Insets(15));
dialogStage.setScene(new Scene(vbox));
dialogStage.show();
}
//****************************************************************************
public void showPW() {
myShowPWLabel.setText(myPWField.getText());
}
public void hidePW() {
myShowPWLabel.setText("");
}
public void exit() {
System.exit(0);
}
public void write() {
PWLabel.setText("Mukodik");
}
public void writeInput(String in) {
password = in;
System.out.println("final password text text: " + password);
writeFinally();
}
public void writeFinally() {
System.out.println("This is 'password' : " + password);
//bottomLabel.setText(password);
}
public void bottomLabelWrite() {
bottomLabel.setText(myPWField.getText());
}
public static void setLabel() throws InterruptedException {
myBottomLabel.setText("");
myBottomLabel.setText("Database has been permanently erased.");
//Thread.sleep(3000);
//System.exit(0);
}
public static void noKeyEnteredNote() {
myBottomLabel.setTextFill(Color.BLACK);
myBottomLabel.setText("No key entered. Type Main Key.");
}
public static void rightKey() {
myBottomLabel.setText("Yes, this is the right key.");
}
public static void wrongKey() throws InterruptedException {
tries = MasterKey.numOfTryLeft;
if (tries > 0) {
myBottomLabel.setTextFill(Color.RED);
myBottomLabel.setText("!!!Wrong key!!! You've got " + tries + " tries left!");
}
}
public void simpleTest(String in) {
System.out.println("in simpleTest and in is: " + in);
}
public void getMainKey() throws IOException, InterruptedException {
MasterKey masterKey = new MasterKey();
System.out.println("Inside SceneController");
masterKey.requestKey(myPWField.getText());
}
public void changeScreen(ActionEvent event) throws IOException, InterruptedException {
getMainKey();
if (MasterKey.isRightKey) {
Parent tableViewParent = FXMLLoader.load(getClass().getResource("Menu.fxml"));
Scene tableViewScene = new Scene(tableViewParent);
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.setScene(tableViewScene);
window.show();
}
}
}
This is Timer class:
package GradleFX;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
public class Timer extends Task {
private ActionEvent actionEvent;
#Override
protected Integer call() throws Exception {
boolean notCalled = true;
while (SceneController.timercount > 0) {
SceneController sceneController = new SceneController();
System.out.println(SceneController.timercount);
Thread.sleep(1000);
SceneController.timercount--;
if (SceneController.timercount < 19) {
System.out.println("Less than 5");
if(notCalled) {
sceneController.popup();
notCalled = false;
}
}
}
System.exit(0);
return null;
}
}
Add this to your code:
#Override
public void initialize(URL location, ResourceBundle resources) {
Timer timerTask = new Timer();
myBottomLabel = bottomLabel;
myPWField = PWField;
myShowPWLabel = showPWLabel;
new Thread(timerTask).start();
timerTask.setOnFinished(e->{
popup();
});
}
I want to constantly display the current mileage of the car without clicking on any button, etc.
Thread class
package threads;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import static controller.GameWindowController.*;
public class MileageThread implements Runnable {
#FXML
Label mileageLabel;
#Override
public void run() {
{
double time = 1;
while (true) {
try {
Thread.sleep(1000);
} catch (NullPointerException | InterruptedException e) {
e.printStackTrace();
}
getCar().setDistancePerSec((time * getCar().getCurrentCarSpeed()) / 3600);
getCar().setCarMileage(getCar().getCarMileage() + getCar().getDistancePerSec());
System.out.format("dziala %.3f km przebiegu %n", getCar().getCarMileage());
mileageLabel.setText(String.valueOf(getCar().getCarMileage()));
}
}
}
}
With this record I receive:
Exception in thread "Thread-5" java.lang.NullPointerException
at threads.MileageThread.run(MileageThread.java:27)
and here is the class in which I make the thread:
package controller;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import model.VehicleStatus;
import threads.MileageThread;
import vehicles.Car;
import java.net.URL;
import java.util.ResourceBundle;
public class GameWindowController extends Thread implements Initializable, Runnable {
#FXML
ImageView smoke;
#FXML
private Label speedLabel, mileageLabel;
static Car car;
static {
car = new Car() {
};
}
public void accelerationButtonPushed(ActionEvent event) throws InterruptedException {
car.accelerate();
speedLabel.setText(String.valueOf(car.getCurrentCarSpeed()));
smoke.setFitHeight(smoke.getX() + car.getCurrentCarSpeed());
smoke.setFitWidth(smoke.getY() + car.getCurrentCarSpeed());
System.out.println(car.getCurrentCarSpeed());
System.out.println(car.getCarMileage());
System.out.println(car.getFuel());
System.out.println(car.getStatus());
}
public void stopCarButtonPushed(ActionEvent event) throws InterruptedException {
if (car.getCurrentCarSpeed() == 0) {
System.out.println("Silnik zgasl!");
}
car.stop();
speedLabel.setText(String.valueOf(car.getCurrentCarSpeed()));
smoke.setFitHeight(smoke.getX() + car.getCurrentCarSpeed());
smoke.setFitWidth(smoke.getY() + car.getCurrentCarSpeed());
System.out.println(car.getCurrentCarSpeed());
System.out.println(car.getCarMileage());
System.out.println(car.getFuel());
System.out.println(car.getStatus());
}
public void startButtonPushed(ActionEvent event) throws InterruptedException {
if (car.getStatus() != VehicleStatus.MOVING) {
car.start();
} else System.out.println("Samochod juz jest na chodzie!");
System.out.println("Status pojazdu: " + car.getStatus());
}
#FXML
public void initialize(URL location, ResourceBundle resources) {
smoke.setFitHeight(1);
smoke.setFitWidth(1);
MileageThread mileageThread = new MileageThread();
Thread mt = new Thread(mileageThread);
mt.start();
}
public static Car getCar() {
return car;
}
}
I apologize if you find here some basic mistakes or if I misunderstood you.
I'm using a scene builder and I just want to put the text in one of the labels using MileageThread.
If you want to modify the user interface from a thread other than the JavaFX Application thread you must wrap it in a call to Platform.runLater()
Platform.runLater(() -> {
userInterfaceControl.setText("New Text");
});
JavaFX nodes/controls/etc. can only be modified from the JavaFX Application thread.
The solution that worked for me:
#FXML
public void initialize(URL location, ResourceBundle resources) {
PauseTransition wait = new PauseTransition(Duration.seconds(1));
wait.setOnFinished((e) -> {
mileageLabel.setText(String.valueOf(getCar().getCarMileage()));
wait.playFromStart();
});
wait.play();
}
I hope that someone will help :)
I'm new to JavaFX and trying build a simple client application that has a root landing stage (rootLayout) with buttons that will allow me to swap out the RootLayout.FXML with a sub FXML. My issue begins when I attempt to load a TreeView within the sub FXML pane. The TreeView is used to load a file directory within this area of the sub-pane.
Thank you!!!
Main
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class MainApp extends Application {
public static Properties configProp;
private Stage primaryStage;
private static BorderPane rootLayout;
/**
* Just a root getter for the controller to use
*/
public static BorderPane getRoot() {
return rootLayout;
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
initRootLayout();
}
public static void main(String[] args) {
configProp = loadProperties();
launch(args);
}
/***
* Initialize the root layout
* #param args
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
RootLayoutController
import java.io.IOException;
import einMyQueue.MainApp;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.BorderPane;
public class RootLayoutController {
// Reference to the main application
private MainApp mainApp;
/**
* Is called by the main application to give a reference back to itself.
*
* #param mainApp
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
/**
* Selects the FAST Scene
*/
#FXML
private void handleFASTSceneSelection() {
try {
SplitPane paneOne = FXMLLoader.load(getClass().getResource("FASTMessageRequest.fxml"));
BorderPane border = MainApp.getRoot();
border.setCenter(paneOne);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Sub-Controller that implements TextView
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import einMyQueue.MainApp;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.stage.DirectoryChooser;
public class FASTMessageRequestController {
private MainApp mainApp;
// FXML Annotations
#FXML private TreeView<String> msgDirTreeView;
#FXML private TextArea msgBodyTextArea;
#FXML private TextArea consoleTextArea;
public void initialize() {
// Using a Tree View
msgDirTreeView = new TreeView<String>();
DirectoryChooser dirChooser = new DirectoryChooser();
URL url = getClass().getResource("../resources/templates");
try {
dirChooser.setInitialDirectory(new File(url.toURI()));
File file = dirChooser.getInitialDirectory();
msgDirTreeView.setRoot(getNodesForDirectory(file));
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public TreeItem<String> getNodesForDirectory(File directory) {
TreeItem<String> msgDirRoot = new TreeItem<String>(directory.getName());
for(File iFile : directory.listFiles()) {
System.out.println("Loading " + iFile.getName());
if(iFile.isDirectory()) {
msgDirRoot.getChildren().add(getNodesForDirectory(iFile));
} else {
msgDirRoot.getChildren().add(new TreeItem<String>(iFile.getName()));
}
}
return msgDirRoot;
}
/***
* Name: setMainApp
* Purpose: Is called by the main application to give a reference back to itself.
*
* #param mainApp
*/
public void setMainApp(MainApp mainApp) {
// Is called by the main application to give a reference back to itself.
this.mainApp = mainApp;
}
}
I have spent the last couple of days looking into this and trying my hand at some different solutions found but Im still having a hard time implementing them correctly.
I have a project that's bound to turn into something mid-sized so I want to make sure I am using MVC correctly from the start instead of just hacking it apart "making it work".
As of now the application will only open 3 scenes, 2 on start(no fxml needed) and another for settings(using a FXML). I do need to be careful of strong references to these as this application will be running on low resource devices.
Ultimately my questions are:
Am I using scenes and controllers correctly? and given the way I have initiated the settings scene Im not finding a way to edit it with a controller. What am I doing wrong?
Below is what I have so far and I must say I dont like the way I initialize the first 2 scenes from Main and of course trying to start the settings scene is rather haphazard. My hurdle now is trying to interact with the scenes through their controllers.
Main.java
package sample;
import javafx.application.Application;
import javafx.stage.Stage;
import sample.controllers.StageController;
<pre>
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
StageController newStage = new StageController();
newStage.start(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
StageController.java
package sample.controllers;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.List;
import static sample.controllers.HotkeyController.createHotkeys;
import static sample.controllers.WebViewController.createWebView;
/**
* Created by devin on 4/14/16.
*/
public class StageController extends Application
{
#Override
public void start(Stage primaryStage)throws Exception
{
Screen primaryScreen = Screen.getPrimary();
Screen secondaryScreen;
List<Screen> allScreens = Screen.getScreens();
if (allScreens.size() <= 1)
{
System.out.println("Only one screen");
secondaryScreen = primaryScreen;
}
else
{
if (allScreens.get(0).equals(primaryScreen))
{
secondaryScreen = allScreens.get(1);
}
else
{
secondaryScreen = allScreens.get(0);
}
}
configureStage("Primary", primaryStage, primaryScreen);
final Stage secondaryStage = new Stage();
configureStage("Secondary", secondaryStage, secondaryScreen);
}
private void configureStage(final String name, final Stage stage, final Screen screen)
{
Rectangle2D bounds = screen.getBounds();
System.out.println(name + ":" + bounds);
stage.setX(bounds.getMinX());
stage.setY(bounds.getMinY());
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.initStyle(StageStyle.UNDECORATED);
showStage(name, stage, screen);
stage.show();
stage.setFullScreen(true);
}
private void showStage(final String name, final Stage stage, final Screen screen)
{
//Scene scene = new Scene(new Group());
StackPane root = new StackPane();
Scene scene = new Scene(root);
stage.setScene(scene);
createWebView(name, root);
scene.setRoot(root);
/*
* Use hotkeys to allow application actions
*/
createHotkeys(name, scene);
}
}
WebViewController
package sample.controllers;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.w3c.dom.Document;
/**
* Created by devin on 4/13/16.
*/
public class WebViewController
{
public static void createWebView(final String name, final StackPane root)
{
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
if (name == "Primary") {
webEngine.load("http://google.com");
}
else
{
webEngine.load("http://google.com");
}
webEngine.documentProperty().addListener(new ChangeListener<Document>()
{
#Override public void changed(ObservableValue<? extends Document> prop, Document oldDoc, Document newDoc)
{
String heightText = browser.getEngine().executeScript(
"window.getComputedStyle(document.body, null).getPropertyValue('height')"
).toString();
double height = Double.valueOf(heightText.replace("px", ""));
System.out.println("Height of browser on " + name + " " + height);
}
});
root.getChildren().addAll(browser);
/* This is a firebug call if we need to debug the webpage that is being loaded */
// webEngine.documentProperty().addListener(new ChangeListener<Document>() {
// #Override public void changed(ObservableValue<? extends Document> prop, Document oldDoc, Document newDoc) {
// enableFirebug(webEngine);
// }
// });
}
private static void enableFirebug(final WebEngine engine) {
engine.executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}");
}
}
HotkeyController.java
package sample.controllers;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
/**
* Created by devin on 4/13/16.
*/
public class HotkeyController
{
public static void createHotkeys(final String name, final Scene scene)
{
final KeyCombination exitCMD = new KeyCodeCombination(KeyCode.E, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
if (exitCMD.match(event))
{
System.out.println("CTRL + E was pressed on " + name + " display\n" +
"Exiting the application");
Platform.exit();
}
}
});
final KeyCombination settingsCMD = new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
if (settingsCMD.match(event))
{
System.out.println("CTRL + S was pressed on " + name + " display\n" +
"Opening Settings Scene");
/*
* This is where we need to launch a scene for settings
*/
try
{
Parent root = FXMLLoader.load(getClass().getResource("/sample/view/settingsscreen.fxml"));
Stage settingsStage = new Stage();
settingsStage.setTitle("FTX Signage Settings");
settingsStage.setScene(new Scene(root, 500 , 400));
settingsStage.show();
// SettingsController settingsController = new SettingsController();
// GettersSetters getSet = new GettersSetters();
// settingsController.getText();
// String hostname = getSet.getHostname();
// settingsController.setText(hostname);
} catch (Exception e)
{
e.printStackTrace();
}
}
}
});
}
}
I try to create a label on click for my PieChart, but unfortunately my label is never visible.
I found a similar topic on StackOverFlow : Label not showing on mouse event JavaFx
But my application is not as simple. I can't add my Label to the list of children because of my architecture.
(You can found a diagram here : http://i.stack.imgur.com/ZFJaR.png )
Here my code :
PieChartNode.java
package nodeStatsVision.chartFactory;
import java.util.ArrayList;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import nodeStatsVision.beans.ListRepere;
import nodeStatsVision.beans.OptionsChart;
import nodeStatsVision.beans.ValueStat;
/**
*
* #author Zombkey.
*/
public class PieChartNode implements ChartNode {
private ListRepere categories;
private ArrayList<ValueStat> values;
private ObservableList<PieChart.Data> pieChartData;
private Node node;
public PieChartNode(ListRepere categories, ArrayList<ValueStat> values){
this.categories = categories;
this.values = values;
pieChartData = FXCollections.observableArrayList();
node = new PieChart(pieChartData);
Platform.runLater(new Runnable() {
#Override
public void run() {
formatData();
}
});
}
private void formatData() {
final Label caption = new Label("");
caption.setTextFill(Color.DARKORANGE);
caption.setStyle("-fx-font: 24 arial;");
for(ValueStat v : values){
PieChart.Data dataTemp = new PieChart.Data(v.getCategorie().getStringName(),v.getDoubleValue());
pieChartData.add(dataTemp);
dataTemp.getNode().addEventHandler(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
System.out.println("event : "+v.getCategorie().getStringName()+" : "+v.getDoubleValue());
caption.setTranslateX(e.getSceneX());
caption.setTranslateY(e.getSceneY());
caption.setText(String.valueOf(dataTemp.getPieValue()));
caption.setVisible(true);
System.out.println("label "+caption);
}
});
}
}
#Override
public Node getNodeGraph() {
return node;
}
#Override
public void setOptions(OptionsChart optionsChart) {
//To implemente
}
}
Have you a idea about, how add my Label to the scene ?
Thanks !
(Other question, Why the Node of PieChart.Data is on ReadOnly ?)
Zombkey.
PS : Sorry about my english, I'm a French student, I'm still learning :)
Ps 2 : First time on StackOverflow, if I did mistake, tell me it !
Ok ! I found a solution for my case !
Semantically my Label is only for my PieChart. That's why I don't want had it to my SceneGraph.
My ChartFactory return a Node, then display it. So my node have to contain the PieChart AND the Label.
I create a Group with a StackPane. In the StackPane I add my PieChart and my Label. Then my factory return the Group as a Node.
Drop the code !
package nodeStatsVision.chartFactory;
import java.util.ArrayList;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import nodeStatsVision.beans.ListRepere;
import nodeStatsVision.beans.OptionsChart;
import nodeStatsVision.beans.ValueStat;
/**
*
* #author Zombkey.
*/
public class PieChartNode implements ChartNode {
private ListRepere categories;
private ArrayList<ValueStat> values;
private ObservableList<PieChart.Data> pieChartData;
private Group group;
private Node node;
private final Label caption;
public PieChartNode(ListRepere categories, ArrayList<ValueStat> values){
this.categories = categories;
this.values = values;
group = new Group();
StackPane pane = new StackPane();
group.getChildren().add(pane);
pieChartData = FXCollections.observableArrayList();
node = new PieChart(pieChartData);
pane.getChildren().add(node);
caption = new Label("");
caption.setVisible(false);
caption.getStyleClass().addAll("chart-line-symbol", "chart-series-line");
caption.setStyle("-fx-font-size: 12; -fx-font-weight: bold;");
caption.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
pane.getChildren().add(caption);
Platform.runLater(new Runnable() {
#Override
public void run() {
formatData();
}
});
}
private void formatData() {
for(ValueStat v : values){
PieChart.Data dataTemp = new PieChart.Data(v.getCategorie().getStringName(),v.getDoubleValue());
pieChartData.add(dataTemp);
dataTemp.getNode().addEventHandler(MouseEvent.MOUSE_ENTERED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
caption.setTranslateX(e.getX());
caption.setTranslateY(e.getY());
caption.setText(String.valueOf(dataTemp.getPieValue()));
caption.setVisible(true);
}
});
dataTemp.getNode().addEventHandler(MouseEvent.MOUSE_EXITED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
caption.setVisible(false);
}
});
}
}
#Override
public Node getNodeGraph() {
return (Node)group;
}
#Override
public void setOptions(OptionsChart optionsChart) {
//To implemente
}
}
Thanks #eckig for your answers !
You create and style your Label named caption but never add it to the SceneGraph.
Somewhere it has to be added to a Parent element, otherwise it will not get displayed.
Your PieChart gets added to a parent element, otherwise it will not be displayed. The same way goes for all other JavaFX Nodes.
As to your second question, read the JavaDocs:
Readonly access to the node that represents the pie slice. You can use this to add mouse event listeners etc.
You could use Tooltip to display a value:
for (final PieChart.Data temp : pieChart.getData()) {
Tooltip tooltip = new Tooltip(String.valueOf(temp.getPieValue()));
Tooltip.install(temp.getNode(), tooltip);
}