Is using fx:root a requirement to setting a controller programmatically? - javafx-2

Everywhere I see explanations on the use o FXMLLoader#setController() it's associated with using fx:root and also setting a root node programmatically (both Oracle Docs and SO answers have this pattern).
Is it a requirement? Or can I create a regular FXML (probably using SceneBuilder) with some good old container and set only the controller programmatically later?
In FXML:
<BorderPane fx:id="root" prefHeight="500.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" > </Borderpane>
In some code (probably a controller):
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml_example2.fxml"));
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}

I don't think it is a requirement. I've got this working by tweaking the Oracle tutorial code to look like this in my Application class:
#Override
public void start(Stage stage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml_example.fxml"));
fxmlLoader.setController(new ExampleController());
Parent root = (Parent)fxmlLoader.load();
stage.setTitle("FXML Welcome");
stage.setScene(new Scene(root, 300, 275));
stage.show();
}
As you can see I've set my ExampleController programatically rather than using the fx:controller="ExampleController" in the FXML and I didn't have to set id:root anywhere to do it.
As an aside, I quite like this approach as it mimics more closely setting the data context in MVVM with WPF and further decouples the view from the controller.

Related

Can't load library natives/linux-amd64/libgluegen_rt.so with Processing and JavaFX

I want to write an application that runs with two different windows, one with JavaFX (v18) and one with Processing (v4). I'm on Linux 64-bit using IntelliJ-Idea as IDE and Maven to manage dependencies. The project is created as a JavaFX project and the processing.core library is added as a separate project library.
I spent A LOT of TIME understanding how to set up the project correctly and I succeeded in launching a simple JFX app that creates a new window where a 2D sketch runs fine (a circle that follows the mouse's coordinates).
The problem comes out when I try to launch a 3D sketch (because that's what i need for my application) with the P3D render and I receive an exception with:
java.lang.UnsatisfiedLinkError: Can't load library: /home/.../natives/linux-amd64/libgluegen_rt.so
As suggested here I already tried to solve adding the jogl-fat.jar library as a project's library or dependencies for the jogl libraries from maven, without effects.
I don't think that something is wrong with my code but it's Processing or JFX's fault.
PS: I have to use processing to make a 3D environment for simulation, but I don't have restrictions for JavaFX. If someone knows another framework that can let me launch a P3D sketch and interact with it with some controls (i.e. buttons), you are welcome!
This is an example of the java classes that I have:
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
public class ProcessingTest extends PApplet{
private PeasyCam cam;
#Override
public void setup() {
background(255);
cam = new PeasyCam(this, 400);
}
public void settings(){
size(200, 200, P3D);
}
public void draw(){
background(255);
rotateX(-.5f);
rotateY(-.5f);
lights();
scale(5);
strokeWeight(1 / 10f);
fill(96, 255, 0);
box(30);
pushMatrix();
translate(0, 0, 20);
fill(0, 96, 255);
box(5);
popMatrix();
}
public void run(){
String[] args = {"com.effibot.provafx.ProcessingTest"};
PApplet.runSketch(args,this);
}
}
public class HelloController {
#FXML
private Label welcomeText;
private ProcessingTest pt;
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
pt = new ProcessingTest();
pt.run();
}
}
Unfortunately I don't have experience with your exact setup (64-bit Linux, IntelliJ, JavaFX application), but hopefully I can point you in the right direction.
The OpenGL native libraries should ship with Processing:
processing-4.0b8-linux-x64.tar/processing-4.0b8/core/library/linux-amd64
should contain:
libgluegen_rt.so
libnativewindow_awt.so
libnativewindow_drm.so
libnewt_head.so
libnativewindow_x11.so
libjogl_desktop.so
libnewt_drm.so
libjogl_mobile.so
Hopefully the IntelliJ docs can help with the native library setup.
The idea is that you'd tell IntelliJ not only to use core.jar, jogl-all.jar and gluegen-rt.jar but also set the native library paths to point to the .so files.
(as jewelsea mentions, in the run options you could set the native path as a command line argument (e.g. if Processing in unzipped in the home folder -Djava.library.path=/home/processing-4.0b8-linux-x64.tar/processing-4.0b8/core/library/linux-amd64) but I hope IntelliJ's interface is straight forward enough to easily allow you to set the native library path for the gluegen and jogl libraries)
(Also, bare in mind Processing ships with it's own JDK packaged (in processing-4.0b8/java): in case you run into some java version related issues you could switch the JDK in IntelliJ))

FXML parent element not injected in child controller [duplicate]

I am having the following problem with a program that I am currently writing, and I have searched on the internet, but I couldn't really find anything to help me understand the following problem
So inside another class I have written a method that executes this whenever the search button is clicked and the method looks like this:
public void searchButton(){
try {
new SearchController().display();
} catch (IOException e) {
e.printStackTrace();
}
}
And then the SearchController class looks something like this (I simplified it here):
public class SearchController {
#FXML
private Button cancelButton;
#FXML
private Label what;
private static Stage stage;
private static BorderPane borderPane;
#FXML
public void initialize(){
what.setText("Testing"); // this woks
cancelButton.setOnAction(e -> stage.close());
}
public void display() throws IOException {
stage = new Stage();
stage.setResizable(false);
stage.setTitle("Product search");
stage.initModality(Modality.APPLICATION_MODAL);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(SearchController.class.getResource("Search.fxml"));
borderPane = loader.load();
Scene scene = new Scene(borderPane);
stage.setScene(scene);
//what.setText("Testing") and this doesn't work
stage.showAndWait();
}
}
Can someone please tell me why it is possible to write text on the initialize method (that method gets called after the borderPane = loader.load(); line...so why doesn't it work if I try to write on the label after that line?)
Thank you in advance
The FXMLLoader creates an instance of the class specified in the fx:controller attribute of the FXML root element. It then injects the elements defined in the FXML file into the controller instance it created when the fx:id attributes match the field names. Then it calls the initialize() method on that instance.
You create an instance of the controller "by hand" with new SearchController(). This is not the same object that is created by the FXMLLoader. So now when you have loaded the fxml file you have two different instances of SearchController. So if you call what.setText(...) from the display() method, you are not calling it on the controller instance created by the FXMLLoader. Consequently, what has not been initialized in the instance on which you are calling what.setText(...), and you get a null pointer exception.
Since initialize() is invoked by the FXMLLoader on the instance it created, when you call what.setText(...) from the initialize() method, you are calling it on the instance created by the FXMLLoader, and so the FXML-injected fields for that instance have been initialized.

Popup window Label is not getting loaded

1.Trying to display exception message in popup window. Exception message is not appearing.
2.Eg: When i click button a popup window (Second fxml file) is to load with proper exception message in the label
3.Popup window is appearing, But the Label is not loading (Bold one --> ExceptionLabel.setText("Please enter Proper file path")) it says null pointer exception.
4.I am not sure what i missing. Same declared in FX:ID and also in second fxml file linked the main controller. Thanks in advance.
#FXML
public Label ExceptionLabel;
Stage PopupWindow = new Stage();
public void Buttonhandle(ActionEvent event) throws IOException {
try {
if(ESBOutboundFile!=null && OutputFile!=null){
String Output = SBlogpaser.Logpaser(ESBInboundFile,ESBOutboundFile,OutputFile);
System.out.println(Output);
}else{
Window(PopupWindow);
**ExceptionLabel.setText("Please enter Proper file path");**
}
} catch (Exception ex) {
System.out.println(ex);
}
}
public void Window(Stage Popup) throws Exception {
this.Popup=Popup;
final FXMLLoader fxmlLoader = new FXMLLoader();
Parent root= fxmlLoader.load(getClass().getResource("POPUPWindow.fxml"));
Scene scene1 = new Scene(root);
Popup.setScene(scene1);
Popup.show();
}
If i keep the label in "OK" handle button it is getting displayed.
From where are you expecting ExceptionLabel to be instantiated?
Assuming you are pointing the fx:controller attribute of the root of your POPUPWindow.fxml file to the current class, it will just create a new instance of that class, and inject values into that instance. The field ExceptionLabel in the current instance won't be initialized.
You could probably just about make this work by setting the controller of the FXMLLoader to the current object, something like this:
public void window(Stage popup) throws Exception {
this.popup=popup; // why?
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("POPUPWindow.fxml"));
fxmlLoader.setController(this);
Parent root= fxmlLoader.load();
Scene scene1 = new Scene(root);
popup.setScene(scene1);
popup.show();
}
and then remove the fx:controller attribute from POPUPWindow.fxml.
This seems like a really bad idea, though, because now the current object is acting as a controller for two different FXML files. This will be confusing at best, and under fairly reasonable conditions would produce weird results. It would be much better to write a different controller class for the popup:
public class PopupController {
private final String message ;
#FXML
private Label exceptionLabel ;
public PopupController(String message) {
this.message = message ;
}
public void initialize() {
exceptionLabel.setText(message);
}
}
and then use the window(...) method above, but with
fxmlLoader.setController(new PopupController("Please enter Proper file path"));
Obviously, if you're reusing the window(..) method, you might want to pass the message in as a parameter to that method.

JavaFX "already set as root of another scene" exception when launching modal dialog a second time

I am building a simple JavaFX application that requires me to launch a modal window in front of my main application window. Using the code below, I am able to launch the modal window 1 time and close it. If I attempt to launch it again, I receive:
java.lang.IllegalArgumentException: BorderPane[id=root, styleClass=root]is already set as root of another scene
I am using the Spring Controller/FXML View dependency injection method described here:
http://www.zenjava.com/2012/02/20/porting-first-contact-to-spring/
I am able to programatically create a scene and hide/display a simple dialog without using FXML / Spring Controller injection. This works fine.
I am unable to explain the 'already set as root' exception, as I am creating a new Scene() each time the startButton is clicked. The 1st Scene should have been destroyed whenever the modal window was closed the 1st time.
Relevant files are below.
MainTabPanel.java - The main view of my application. This contains the 'startButton' that is clicked to launch the modal window.
The ActivePresentation Controller/View is injected as:
#Inject private ActivePresentation activePresentation;
Below is the initialize() method that attempts to launch the modal when startButton is clicked.
#FXML
public void initialize()
{
availableReceiversIdColumn.setCellValueFactory(new PropertyValueFactory("id"));
availableReceiversFirmwareVersionColumn.setCellValueFactory(new PropertyValueFactory("firmwareVersion"));
availableReceiversModelColumn.setCellValueFactory(new PropertyValueFactory("model"));
availableReceiversChannelColumn.setCellValueFactory(new PropertyValueFactory("channel"));
ObservableList<String> responseTypes = FXCollections.observableArrayList();
responseTypes.add("Single Response Alpha");
responseTypes.add("Single Response Numeric");
responseTypeChoiceBox.setItems(responseTypes);
startButton.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
presentationResponseService.startPresentation();
activePresentation.populateResponses(null);
activePresentation.populateResults(null);
Scene activePresentationScene = new Scene(activePresentation.getView());
activePresentationScene.getStylesheets().add("styles.css");
stage.setScene(activePresentationScene);
stage.setTitle("Active Presentation");
stage.showAndWait();
}
});
}
The closeButton is defined in the modal dialog as follows.
closeButton.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
presentationResponseService.closePresentation();
Stage stage = (Stage) root.getScene().getWindow();
stage.close();
}
});
The Java based Spring configuration for the ActivePresentation bean and FXML loader are as follows.
#Bean
public ActivePresentation activePresentation()
{
return loadPresenter("/fxml/ActivePresentation.fxml");
}
FXML Loader
private <T> T loadPresenter(String fxmlFile)
{
try
{
FXMLLoader loader = new FXMLLoader();
loader.load(getClass().getResourceAsStream(fxmlFile));
return (T) loader.getController();
} catch (IOException e)
{
throw new RuntimeException(String.format("Unable to load FXML file '%s'", fxmlFile), e);
}
}
I had the issue when I had a pop-up window autowired. it was working the first time but when I close the pop-up window the second time, I was getting this error.
I managed by checking if the pop-up window had already a scene, see the below code:
if (window.getScene() == null) {
Scene scene = new Scene(window);
stage.setScene(scene);
} else {
stage.setScene(window.getScene());
}
Seems, the stage is still under control of JFX (logic : you can try yo open it again).
And you create a stage each time while previous is still alive, so, could you try to add stage.setScene(null) near stage.close().
Or use the same scene or the same stage each time?
I don't see any bug in this part of JFX : node is the same, stages (and scenes in it) are different. So there are 2 ways : use only 1 stage+scene, or create different border pane instances each time.

Common menu item for all scenes in Java FX

I want to develop a multiple scenes Java FX application. But I want to have the common menu across all scenes. I think using FXML I can create a menu in a scene. But Can I have the same menu across all the scenes even after I navigated to other screen?
If so how is it. Else let me know any alternative for it.
Yes this is possible. I'm using this mechanism in my own application.
What I do first is make an FXML with the menu bar and an AnchorPane who contains the content. This FXML is loaded when the application starts.
I use a Context class (based on the answer of Sergey in this question: Multiple FXML with Controllers, share object) which contains a method ShowContentPane(String url) method:
public void showContentPane(String sURL){
try {
getContentPane().getChildren().clear();
URL url = getClass().getResource(sURL);
//this method returns the AnchorPane pContent
AnchorPane n = (AnchorPane) FXMLLoader.load(url, ResourceBundle.getBundle("src.bundles.bundle", getLocale()));
AnchorPane.setTopAnchor(n, 0.0);
AnchorPane.setBottomAnchor(n, 0.0);
AnchorPane.setLeftAnchor(n, 0.0);
AnchorPane.setRightAnchor(n, 0.0);
getContentPane().getChildren().add(n);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
So what basically happens is:
When the program starts, set content pane in the Context:
#Override
public void initialize(URL url, ResourceBundle rb) {
Context.getInstance().setContentPane(pContent); //pContent is the name of the AnchorPane containing the content
...
}
When a button or menuitem is chosen, I load the FXML in the content Pane:
#FXML
private void handle_FarmerListButton(ActionEvent event) {
Context.getInstance().showContentPane("/GUI/user/ListUser.fxml");
}
Hope this helps :)

Resources