Issue with passing constructor parameter via controller factory - javafx

I've got an issue with passing parameters to controller. The main program uses a controller factory to pass a stage object to my controller. The controller factory prints the address of the stage object, but the controller gets Null as stage object. Why ?
Though I've reduced my application to a simple dialogue, I can't find my fault. I hope you can help. Thanks!
The main program:
public class Main extends Application {
private final Logger logger = Logger.getLogger(this.getClass().getName());
private final String FXML_SIMPLE_DIALOG = "testDialog.fxml";
private MyHandler myHandler = new MyHandler();
#Override
public void init() {
try{
// Thread.currentThread is the FX-Launcher thread:
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try {
logger.addHandler(new FileHandler("java.myLOG"));
}
catch (IOException e) {
throw new IllegalStateException("IOException when adding File Handler");
}
}
catch (Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
#Override
public void start(Stage primaryStage) {
try{
logger.info("Test Application started");
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try{
URL location = new URL(this.getClass().getResource("resources/fxml/" + FXML_SIMPLE_DIALOG).toString());
FXMLLoader loader = new FXMLLoader(location);
loader.setControllerFactory(new SimpleControllerFactory(primaryStage));
Pane root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("*** TEST App ***");
primaryStage.show();
}
catch(IOException ex) {
ex.printStackTrace();
throw new IllegalStateException("check program source code!");
}
}
catch(Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
public static void main(String[] args) {
launch(args);
}
class MyHandler implements Thread.UncaughtExceptionHandler{
#Override
public void uncaughtException(Thread thread, Throwable throwable) {
logger.log(Level.SEVERE, "** STOPP ** due to uncaught exception", throwable);
Platform.exit();
}
}
}
The code of the controller factory:
public class SimpleControllerFactory implements Callback<Class<?>,Object> {
private static final Logger logger = Logger.getLogger(SimpleControllerFactory.class.getName());
private final Stage primaryStage;
public SimpleControllerFactory(Stage stage) {
this.primaryStage = stage;
System.out.println("controller factory: value of stage: " + this.primaryStage);
}
public SimpleControllerFactory() { this(null); }
#Override
public Object call(Class<?> type) {
try {
for (var c : type.getConstructors()) {
switch(c.getParameterCount()) {
case 0 -> {}
case 1 -> {
if ( c.getParameterTypes()[0] == Stage.class) {
return c.newInstance(primaryStage) ;
}
else;
}
default -> {}
}
}
return type.getDeclaredConstructor().newInstance();
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
logger.log(Level.SEVERE,ex.toString(),ex);
throw new RuntimeException(ex);
}
}
}
This are the FXML-file and the simple controller:
<VBox prefHeight="150.0" prefWidth="250.0" spacing="5.0" style="-fx-padding: 5 5 5 5;-fx-font-size: 11px"
xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="TestSimpleDialog.DialogController">
<children>
<ButtonBar buttonOrder="UL_HE+FBIX_NCYOA_R" prefHeight="40.0">
<buttons>
<Button alignment="CENTER" mnemonicParsing="false" onAction="#time" text="time ?">
</Button>
</buttons>
</ButtonBar>
<StackPane VBox.vgrow="ALWAYS">
<children>
<Label fx:id="textTime" alignment="CENTER" text="" />
</children>
</StackPane>
</children>
simple controller:
public class DialogController implements Initializable {
private static final Logger logger = Logger.getLogger(DialogController.class.getName());
private final Stage recentStage;
DialogController(Stage stage) {
this.recentStage = stage;
}
DialogController() { this(null); }
#FXML private Label textTime;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
System.out.println("Controller started, value of recentStage: " + this.recentStage);
}
#FXML
private void time(ActionEvent event) {
textTime.setText(LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy kk:mm:ss")));
}
}

The Class.getConstructors() method
Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.
(my emphasis).
Since your constructors are not declared public, the for loop in the factory's call(...) method iterates zero times, and it defaults to the call to the no-arg constructor.
Just declare the constructors as public and it will work:
public class DialogController implements Initializable {
private final Stage recentStage;
public DialogController(Stage stage) {
this.recentStage = stage;
}
public DialogController() { this(null); }
// ...
}
If you really want to keep the constructors non-public, you can us the getDeclaredConstructor(...) method to retrieve a specific constructor, which does not have to be public:
#Override
public Object call(Class<?> type) {
try {
System.out.println("Number of constructors found: "+type.getConstructors().length);
try {
Constructor<?> c = type.getDeclaredConstructor(Stage.class);
return c.newInstance(primaryStage);
} catch (NoSuchMethodException e) {
return type.getDeclaredConstructor().newInstance();
}
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
logger.log(Level.SEVERE,ex.toString(),ex);
throw new RuntimeException(ex);
}
}

Related

Update structure JavaFX TreeTableView

I create two windows and pass the Instance controller to the child window as a parameter. My problem is updating on the fly. I tried calling the refresh() method on treeTblState but nothing came of it.
First window
<AnchorPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mo.specdoc.controllers.StateController">
<children>
<TreeTableView fx:id="treeTblState" showRoot="false">
<columns>
....
</columns>
</TreeTableView>
</children>
</AnchorPane>
Controller
public class StateController implements Initializable {
private static Map<Long, TreeItem> subdivisions = new HashMap<>();
private static StateController instance;
#FXML private TreeTableColumn<StateEntity, String> tblClmnTitle,...,tblClmnDelete;
#FXML private TreeTableView<StateEntity> treeTblState = new TreeTableView<>();
//Create root element (property setShow = false в FXML)
private StateEntity root = new StateEntity(0L,"State");
//Pattern Instance
public static StateController getInstance() {
if (instance == null) {
instance = new StateController();
}
return instance;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
try {
tblClmnTitle.setCellValueFactory(new TreeItemPropertyValueFactory<>("postName"));
...
tblClmnDelete.setCellValueFactory(new TreeItemPropertyValueFactory<>("delete"));
treeGreate();
} catch ....
}
public void addChildren(StateEntity state) {
TreeItem<StateEntity> item = null;
for (Long key : subdivisions.keySet()) {
if (key == state.getSubdivisionId()) {
TreeItem<StateEntity> subdiv = subdivisions.get(key);
item = new TreeItem<StateEntity>(state);
subdiv.getChildren().add(item);
}
}
}
private void treeGreate() {
TreeItem<StateEntity> itemRoot = new TreeItem<StateEntity>(root);
treeTblState.setRoot(itemRoot);
//create tree - level 2
//subdivisions
for (SubdivisionEntity subdivision : subdivisionDAO.findAll()) {
StateEntity state = new StateEntity(
subdivision.getId(),
subdivision.getTitle()
);
state.getAdd().setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
createScene("Add post", new StateEntity(), subdivision.getId());
}
});
TreeItem<StateEntity> subdiv = new TreeItem<StateEntity>(state);
itemRoot.getChildren().add(subdiv);
subdivisions.put(state.getSubdivisionId(), subdiv);
//posts
List<StateEntity> childrens = stateDAO.findByIdSubdiv(state.getSubdivisionId());
if (!childrens.isEmpty()) {
for (StateEntity child : childrens) {
TreeItem<StateEntity> item = new TreeItem<StateEntity>(child);
subdiv.getChildren().add(item);
}
}
}
}
private void createScene(String title, StateEntity state, Long subdivisionId) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/state-edit.fxml"));
StateEditController stateEditController = new StateEditController(state, subdivisionId);
loader.setController(stateEditController);
Stage stage = new Stage();
Scene scene = new Scene(loader.load());
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
} catch ....
}
}
Result
Second window
<AnchorPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
<children>
...elements...
</children>
</AnchorPane>
Controller
public class StateEditController implements Initializable {
....
#FXML
void saveAction(ActionEvent event) {
currentEntry.setSubdivisionId(subdivisionId);
currentEntry.setMaxAmountPersonal(cmbBoxMaxAmount.getSelectionModel().getSelectedItem().intValue());
currentEntry.setMinAmountPersonal(cmbBoxMaxAmount.getSelectionModel().getSelectedItem().intValue());
currentEntry.setAmplification(tglSwitchAmpl.isSelected());
currentEntry.setPostId(cmbBoxPost.getSelectionModel().getSelectedItem().getId());
if (currentEntry.getId() == null) {
stateDAO.save(currentEntry);
} else {
stateDAO.update(currentEntry);
}
stateController.addChildren(currentEntry); //call instance metod
}
#Override
public void initialize(URL location, ResourceBundle resources) {
....
}
}
Result
Everything is entered into the database successfully, but it is not updated when the window is closed 2.
I redraw the child elements using the clear method when adding an item
public void addChildren(StateEntity state) {
subdivisions.get(state.getSubdivisionId()).getChildren().clear();
List<StateEntity> childrens = stateDAO.findByIdSubdiv(state.getSubdivisionId());
if (!childrens.isEmpty()) {
for (StateEntity child : childrens) {
TreeItem<StateEntity> item = new TreeItem<StateEntity>(child);
subdivisions.get(state.getSubdivisionId()).getChildren().add(item);
}
}
}

Read variables from controller of fxml-file

I try to set the size of a pane with a value that is inside a variable of the controller but i always get a:
java.lang.IllegalAccessException: class javafx.fxml.FXMLLoader$ValueElement (in module javafx.fxml) cannot access a member of class blub.Blub with modifiers "private"
EDIT:
I tried to make a minimal reproducible example and made some changes according to your comments. still the same IllegalAccessException
startFXMain.java
public class startFXMain extends Application {
private static MRE myApp;
public static void main(String[] args) {
try {
myApp = MRE.getInstance(args);
Application.launch(args);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void start(Stage primaryStage) throws Exception {
myApp.start(primaryStage);
}
}
MRE.java
public class MRE extends Application {
private static MRE instance = null;
#Override
public void start(Stage startStage) throws Exception {
try {
new MREGUI(startStage);
} catch (Exception e) {
e.printStackTrace();
}
}
public static MRE getInstance(String[] args) {
if (Objects.nonNull(instance))
return instance;
else synchronized (MRE.class) {
if (Objects.isNull(instance))
instance = new MRE();
return instance;
}
}
public static MRE getInstance() { return instance; }
private MRE() {
}
}
MREGUI.java
public class MREGUI extends Application {
private GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice();
public int iScreenWidth = gd.getDisplayMode().getWidth();
public int iScreenHeight = gd.getDisplayMode().getHeight();
private Scene scMainScene;
private Pane pMainPane;
#Override
public void start(Stage stgMainStage) throws Exception {
this.initStage(stgMainStage);
this.initStartPane();
this.initStartScene(this.pMainPane);
stgMainStage.setScene(this.scMainScene);
stgMainStage.show();
}
private void initStage(Stage s) {
s.setX(0);
s.setY(0);
s.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent e) {
Platform.exit();
System.exit(0);
}
});
}
private void initStartPane() {
FXMLLoader loader = new FXMLLoader();
try {
URL xmlUrl = new File(System.getProperty("user.dir") + "\\src\\startwindow.fxml").toURI().toURL();
loader.setLocation(xmlUrl);
pMainPane = loader.load();
loader.setController(this);
} catch (MalformedURLException err) {
err.printStackTrace();
} catch (IOException err) {
err.printStackTrace();
}
}
private void initStartScene(Pane rootPane) {
scMainScene = new Scene(rootPane, iScreenHeight, iScreenWidth);
}
public MREGUI(Stage stgMainStage) {
try {
this.start(stgMainStage);
} catch (Exception err) {
err.printStackTrace();
}
}
private MREGUI() {
}
#FXML
public int getIScreenWidth() { return this.iScreenWidth; }
#FXML
public int getIScreenHeight() { return this.iScreenHeight; }
}
startwindow.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<Pane layoutX="0" layoutY="0" minHeight="640" minWidth="480" prefHeight="${iScreenHeight}" prefWidth="${iScreenWidth}"
xmlns:fx="http://javafx.com/fxml" fx:controller="MREGUI">
<style>
-fx-background-color: blue;
</style>
</Pane>
The problem is that your no-arg constructor in MREGUI is private, so the FXMLLoader cannot construct an instance of the controller class.
Here is a working version with the code considerably cleaned up, and not relying on AWT classes, etc. This is still probably completely the wrong approach (why not just look up the screen size when you create the scene, and set the scene size as required?).
public class StartFXMain {
public static void main(String[] args) {
Application.launch(MRE.class, args);
}
}
public class MRE extends Application {
#Override
public void start(Stage startStage) throws Exception {
try {
new MREGUI(startStage);
} catch (Exception e) {
e.printStackTrace();
}
}
public MRE() {
}
}
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
public class MREGUI {
private Screen screen = Screen.getPrimary();
private double screenWidth = screen.getVisualBounds().getWidth();
private double screenHeight = screen.getVisualBounds().getHeight();
private Scene mainScene;
private Pane mainPane;
public void start(Stage stgMainStage) throws Exception {
this.initStage(stgMainStage);
this.initStartPane();
this.initStartScene(this.mainPane);
stgMainStage.setScene(this.mainScene);
stgMainStage.show();
}
private void initStage(Stage s) {
s.setX(0);
s.setY(0);
}
private void initStartPane() {
FXMLLoader loader = new FXMLLoader();
try {
URL xmlUrl = getClass().getResource("startwindow.fxml");
loader.setLocation(xmlUrl);
mainPane = loader.load();
// this has no effect after loading the FMXL:
// loader.setController(this);
} catch (IOException err) {
err.printStackTrace();
}
}
private void initStartScene(Pane rootPane) {
mainScene = new Scene(rootPane, screenHeight, screenWidth);
}
public MREGUI(Stage stgMainStage) {
try {
this.start(stgMainStage);
} catch (Exception err) {
err.printStackTrace();
}
}
public MREGUI() {
}
public double getScreenWidth() { return this.screenWidth; }
public double getScreenHeight() { return this.screenHeight; }
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<Pane layoutX="0" layoutY="0" minHeight="640" minWidth="480" prefHeight="${screenHeight}" prefWidth="${screenWidth}"
xmlns:fx="http://javafx.com/fxml" fx:controller="org.jamesd.examples.sizing.MREGUI">
<style>
-fx-background-color: blue;
</style>
</Pane>
If you need the controller to be the previously-constructed instance of the class, you have to set the controller prior to loading the FXML:
public class MREGUI {
// ...
private void initStartPane() {
FXMLLoader loader = new FXMLLoader();
try {
URL xmlUrl = getClass().getResource("startwindow.fxml");
loader.setLocation(xmlUrl);
loader.setController(this);
mainPane = loader.load();
} catch (IOException err) {
err.printStackTrace();
}
}
// ...
public MREGUI(Stage stgMainStage) {
try {
this.start(stgMainStage);
} catch (Exception err) {
err.printStackTrace();
}
}
// public MREGUI () {
//
// }
// ...
}
In order for this to work, you must remove the fx:controller attribute from the root element of the FXML. This attribute instructs the FXMLLoader to create an instance of the controller class by calling the no-argument constructor, which is incompatible with setting the controller in code:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<Pane layoutX="0" layoutY="0" minHeight="640" minWidth="480" prefHeight="${screenHeight}" prefWidth="${screenWidth}"
xmlns:fx="http://javafx.com/fxml" >
<style>
-fx-background-color: blue;
</style>
</Pane>

issue with using extended JavaFX Class Dialog<R> and initializing the DialogPane

I use an extended Dialog class in my JavaFX application, but I failed to initialize the fields of the DialogPane. It seems the initialize method is never invoked. I make several modifications of the program code, unfortunately I do not have success. Therefore I hope you can help me. Thanks!
The application is reduced to a minimum: the main program starts via ControllerFactory the PortalController. There is a button to open my 'DialogFriZi'. The related DialogPane contains a label. The initializing of this label failed.
main program:
public class Main extends Application {
private final Logger logger = Logger.getLogger(this.getClass().getName());
private final String FXML_PORTAL = "portal.fxml";
private MyHandler myHandler = new MyHandler();
#Override
public void init() {
try{
// Thread.currentThread is the FX-Launcher thread:
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try {
logger.addHandler(new FileHandler("java.myLOG"));
}
catch (IOException e) {
throw new IllegalStateException("IOException when adding File Handler");
}
}
catch (Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
#Override
public void start(Stage primaryStage) {
try{
logger.info("Test Application started");
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try{
URL location = new URL(this.getClass().getResource("resources/fxml/" + FXML_PORTAL).toString());
FXMLLoader loader = new FXMLLoader(location);
loader.setControllerFactory(new SimpleControllerFactory(primaryStage));
Pane root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("*** TEST App ***");
primaryStage.show();
}
catch(IOException ex) {
ex.printStackTrace();
throw new IllegalStateException("check program source code!");
}
}
catch(Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
public static void main(String[] args) {
launch(args);
}
class MyHandler implements Thread.UncaughtExceptionHandler{
#Override
public void uncaughtException(Thread thread, Throwable throwable) {
logger.log(Level.SEVERE, "** STOPP ** due to uncaught exception", throwable);
Platform.exit();
}
}
}
ControllerFactory
public class SimpleControllerFactory implements Callback<Class<?>,Object> {
private static final Logger logger = Logger.getLogger(SimpleControllerFactory.class.getName());
private final Stage primaryStage;
// types of controller
public SimpleControllerFactory(Stage stage) {
this.primaryStage = stage;
}
public SimpleControllerFactory() { this(null); }
#Override
public Object call(Class<?> type) {
try {
for (var c : type.getConstructors()) {
switch(c.getParameterCount()) {
case 0 -> { }
case 1 -> {
if ( c.getParameterTypes()[0] == Stage.class) {
return c.newInstance(primaryStage) ;
}
else;
}
default -> { }
}
}
return type.getDeclaredConstructor().newInstance();
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
logger.log(Level.SEVERE,ex.toString(),ex);
throw new RuntimeException(ex);
}
}
}
PortalController
public class PortalController implements Initializable {
private static final Logger logger = Logger.getLogger(PortalController.class.getName());
private final Stage recentStage;
private ResourceBundle resourceBundle;
public PortalController(Stage stage) {
this.recentStage = stage;
}
public PortalController() {
this(null);
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
}
#FXML
private void start(ActionEvent event) {
DialogFriZi dialog = null;
dialog = new DialogFriZi(recentStage);
Optional<Boolean> result = dialog.showAndWait();
}
}
related FXML file
<VBox prefHeight="500.0" prefWidth="400.0" spacing="5.0" style="-fx-padding: 5 5 5 5;-fx-font-size: 11px" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="TestSimpleDialog.PortalController">
<children>
<HBox alignment="CENTER">
<children>
<Button mnemonicParsing="false" onAction="#start" text="start FriZi dialog">
<HBox.margin>
<Insets top="50.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</children>
the extended Dialog class
public class DialogFriZi extends Dialog<Boolean> {
#FXML private ButtonType okButtonType;
#FXML Label label;
private static final Logger logger = Logger.getLogger(DialogFriZi.class.getName());
private final Stage recentStage;
public DialogFriZi(Stage primaryStage) {
this.recentStage = primaryStage;
runDialog();
}
public DialogFriZi() {
this(null);
}
#FXML private void initialize() {
System.out.println("start initializing");
label.setText("hello");
}
private void runDialog() {
FXMLLoader loader = new FXMLLoader();
try {
loader.setLocation(new URL
(this.getClass().getResource("resources/fxml/dialogFriZi.fxml").toString()));
DialogPane dialogPane = loader.load();
loader.setController(this);
initOwner(recentStage);
initModality(Modality.APPLICATION_MODAL);
setResizable(true);
setTitle("FriZi Dialog");
setDialogPane(dialogPane);
}
catch (IOException e) {
String message = "illegale state due to problem with 'resource dialogFriZi.fxml'";
logger.log(Level.SEVERE,message);
throw new IllegalStateException(message);
}
}
}
related FXML file
<DialogPane prefHeight="400.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
<content>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<Label fx:id="label" text="Label" />
</children></HBox>
</content>
<buttonTypes>
<ButtonType buttonData="OK_DONE" text="ok" />
<ButtonType buttonData="CANCEL_CLOSE" text="Abbrechen" />
</buttonTypes>
The initalize() method is invoked on the controller, if there is one, during the execution of the load() method. You are setting the controller after calling load(), so when load() runs, there is no controller. You need to change the order of the calls:
private void runDialog() {
FXMLLoader loader = new FXMLLoader();
try {
loader.setLocation(new URL
(this.getClass().getResource("resources/fxml/dialogFriZi.fxml").toString()));
loader.setController(this);
DialogPane dialogPane = loader.load();
// loader.setController(this);
initOwner(recentStage);
initModality(Modality.APPLICATION_MODAL);
setResizable(true);
setTitle("FriZi Dialog");
setDialogPane(dialogPane);
}
catch (IOException e) {
String message = "illegale state due to problem with 'resource dialogFriZi.fxml'";
logger.log(Level.SEVERE,message);
throw new IllegalStateException(message);
}
}

how to terminate a javafx.concurrent.Service in javafx 8

Hi this is the structure of my application.
MCVE:
#FXML
void OnSimulateClick(ActionEvent event) throws IOException {
if (event.getSource() == simulatebutton) {
primaryStage = (Stage) simulatebutton.getScene().getWindow();
pane = (Pane) FXMLLoader.load(TDC.class.getResource("view/Simulation.fxml"));
scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
FXML
<Pane prefHeight="500.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mdw.tdc.view.SimulationController" >
<children>
<Button fx:id="abortbutton" layoutX="650.0" layoutY="230.0" onAction="#onAbortClicked" text="Abort" />
<Button fx:id="homebutton" layoutX="650.0" layoutY="330.0"onAction="#onHomeClicked" text="Home" />
<TextArea fx:id="logscreen" layoutX="21.0" layoutY="20.0" prefHeight="395.0" prefWidth="600.0" />
</children>
</Pane>
controller
public class SimulationController implements Initializable {
#FXML
private Button homebutton;
#FXML
private TextArea logscreen;
#FXML
private Button abortbutton;
private Simulate simulate;
#Override
public void initialize(URL location, ResourceBundle resources) {
simulate = new Simulate(list, logscreen);
simulate.setOnCancelled(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
System.out.println("Simulation Aborted by User...");
}
});
simulate.setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
System.out.println("Simulation Failed...");
}
});
simulate.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
System.out.println("Simulation Success...");
}
});
simulate.start();
}
#FXML
void onAbortClicked(ActionEvent event) throws IOException,
InterruptedException {
if (event.getSource() == abortbutton) {
simulate.cancel();
}
}
}
#FXML
void onHomeClicked(ActionEvent event) throws IOException {
if (event.getSource() == homebutton) {
simulate.reset();
/*back to Home screen*/
pane = (Pane) FXMLLoader.load(TDC.class.getResource("view/Simulation.fxml"));
scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Simulate Sercice
public class Simulate extends Service<Void> {
private ObservableList<TestData> list;
private TextArea logscreen;
private ConsoleStream consoleStream;
public Simulate(ObservableList<TestData> list, TextArea logscreen) {
this.list = list;
this.logscreen = logscreen;
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
consoleStream = new ConsoleStream(logscreen);
consoleStream.start();
/*Some Code*/
System.out.println("End of Simulation");
return null;
}
};
}
/* Few other methods called from inside my code inside createTask()>call() method */
// using this method to flag when cancelled
public void isFlagged(boolean b) {
consoleStream.isFlagged(true);
consoleStream.setOnCancelled(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
consoleStream.reset();
}
});
consoleStream.cancel();
}
}
ConsoleStream Service
public class ConsoleStream extends Service<Void> {
private PipedOutputStream outPipedOutputStream, errorPipedOutputStream;
private PipedInputStream outPipedInputStream, errorPipedInputStream;
private TextArea logscreen;
private Console outCon;
public ConsoleStream(TextArea logscreen) {
this.logscreen = logscreen;
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
try {
System.err.flush();
System.out.flush();
outPipedInputStream = new PipedInputStream();
outPipedOutputStream = new PipedOutputStream(
outPipedInputStream);
System.setOut(new PrintStream(outPipedOutputStream));
errorPipedInputStream = new PipedInputStream();
errorPipedOutputStream = new PipedOutputStream(
errorPipedInputStream);
System.setErr(new PrintStream(errorPipedOutputStream));
outCon = new Console(outPipedInputStream, logscreen);
outCon.setOnCancelled(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
// TODO Auto-generated method stub
System.out.println("ConsoleStream Aborted by User...");
outCon.reset();
}
});
outCon.start();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
}
public void isFlagged(boolean b) {
outCon.cancel();
}
}
Console Service
public class Console extends Service<Void> {
private final InputStream inputStream;
private TextArea logscreen;
public Console(PipedInputStream errorPipedInputStream, TextArea logscreen) {
inputStream = errorPipedInputStream;
this.logscreen = logscreen;
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
while(isCancelled()){
inputStream.close();
break;
}
try {
InputStreamReader is = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(is);
while (br.readLine() != null) {
String read = br.readLine();
logscreen.appendText(read + "\n");
}
is.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
}
}
This is the first attempt at JavaFx not sure this is good way of doing. any suggestions are appriciated
Thanks

Changing the text of a label from a different class in JavaFX

This question was already asked here but was not able to find any answers. I have reproduced a similar situation where I would like to change the text of a label from another class using the controller
FXMLDocumentController.java
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("FXMLDocumentController.#handleButtonAction");
label.setText("Hello World!");
Connector.Connecting();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
public void setLabelText(String text)
{
System.out.println("FXMLDocumentController.setLabelText(): Called");
label.setText(text);
}
}
FXMLDocument.fxml
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="demo5.FXMLDocumentController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
Demo5.java
public class Demo5 extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Connector.java
public class Connector {
public static void Connecting() {
try {
System.out.println("Connector.Connecting(): Called");
FXMLLoader loader = new FXMLLoader(FXMLDocumentController.class.getResource("FXMLDocument.fxml"));
loader.load();
FXMLDocumentController controller = (FXMLDocumentController) loader.getController();
controller.setLabelText("Bye World");
} catch (IOException ex) {
Logger.getLogger(Connector.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Output at Console
Connector.Connecting(): Called
FXMLDocumentController.setLabelText(): Called
But could see no changes in the label. Am I missing something major here ?
You can change your Connector class to receive the Controller instance:
public class Connector {
public static void Connecting(FXMLDocumentController controller) {
try {
System.out.println("Connector.Connecting(): Called");
controller.setLabelText("Bye World");
} catch (IOException ex) {
Logger.getLogger(Connector.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("FXMLDocumentController.#handleButtonAction");
label.setText("Hello World!");
Connector.Connecting(this);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
public void setLabelText(String text)
{
System.out.println("FXMLDocumentController.setLabelText(): Called");
label.setText(text);
}
}
Note:
If your Connector is going to take longer to execute whatever it needs to, you might want to use a Task, so you don't freeze your UI. To update the Label, you have to bind the text property and then update the Text value using the updateMessage() method.
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("FXMLDocumentController.#handleButtonAction");
label.setText("Hello World!");
Task<Boolean> connectorTask = new ConnectorTask();
label.textProperty().bind(connectorTask.messageProperty());
connectorTask.setOnSucceeded(e -> {
// this is going to be called if the task ends up without error
label.textProperty().unbind();
});
new Thread(connectorTask).start();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
//public void setLabelText(String text)
//{
// System.out.println("FXMLDocumentController.setLabelText(): Called");
// label.setText(text);
//}
public class ConnectorTask extends Task<Boolean> {
#Override
protected Boolean call() throws Exception {
// ... do whatever you need here
// then you call this method to update the TextProperty from the Label that was bound.
updateMessage("Bye World");
return Boolean.TRUE;
}
}
}
Your Demo5 class and Connector class are both creating unique instances of the FXMLDocumentController via the call to FXMLLoader.load(). The instance in the Demo5 class is being placed in the scene graph and becomes visible. The instance in the connector is not being made visible. When you call setLabelText() it is changing the text for an unseen label. What you may want to do is get the FXMLDocumentController instance in Demo5 and provide it to the Connector class through the constructor or a setter method. You may need to change some things around depending on what the Connector class is used for. Alternatively, you could use the connector class to load the FXML root and controller and provide methods for accessing them, then use those methods in Demo5 to make the scene visible.
I made it in a simple way by defining the Label as static in the FXMLDocumentController.java:
#FXML GridPane myGridPane;
public static Label totLabel = new Label("Total");
and add it to myGridPane in the initialize method of FXMLDocumentController class:
#Override
public void initialize(URL url, ResourceBundle rb) {
myGridPane.add(totLabel, 0, 3);
}
and at any other class you can call the setText() of this label like this:
String message = "this message will appear in the total label";//your string
FXMLDocumentController.totLabel.setText(message);

Resources