I'm trying to learn JavaFX and I'm having some trouble understanding how the JavaFX thread can interact with the rest of my application. I'm using JavaFX for the interface window but I have some other stuff running in the main thread.
I load my FXML from a file I've created with the Scene Builder and I've attached the open method to a MenuItem in Scene Builder.
I've tried having an instance variable in the Editor class and populating it in the constructor as well as the start method but it's always null in the open method. As far as I understand this is because the open method is called from the JavaFX thread. What is the best practice to solve this problem? This goes the other way to, what if I want to access a component if the JavaFX thread in my main thread?
public class Editor extends Application {
private Stage stage;
private FileChooser fileChooser;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setTitle("Editx");
stage.setScene(new Scene(
FXMLLoader.load(this.getClass().getResource("./Editor.fxml")),
1280,
800));
stage.show();
this.fileChooser = new FileChooser();
this.fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("OBJ", "*.obj"));
this.fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("All Files", "*.*"));
}
#FXML
private void open() {
// Both this.fileChooser and this.stage is null here for example
File file = this.fileChooser.showOpenDialog(this.stage);
}
}
Related
I am completly lost atm. I have been working with scenebuilder and javaFX in the past but I am stuck like 5 hours now and I didnt get a step further. Let me explain:
I have a working java Eclipse Project, using maven dependencies
The Main is where I want to use JavaFX or load a fxml into
The programm takes many many VCC Files and extracts the data to put it all together in an excel
The programm works but I cant load a FXML file into the main or even show a pane in there
Now does my Java Main class has to extend Application? I tried both ways - doenst work.
Some example code:
public void start(Stage primaryStage) {
try {
bpmain = new BorderPane(FXMLLoader.load(new File("src\\fxml\\UserInterface.fxml").toURI().toURL()));
primaryStage.setScene(new Scene(bpmain));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
or this (from original Docs)
public void start(Stage stage) {
Circle circ = new Circle(40, 40, 30);
Group root = new Group(circ);
Scene scene = new Scene(root, 400, 300);
stage.setTitle("My JavaFX Application");
stage.setScene(scene);
stage.show();
}
but this start method is just not getting called... where do I put that?
What my Programm should look like is pretty simple actually. I want a small UI Windows that lets you pick a Folder where the VCC data lives in and a OK Button that basically should run the Main method.
So a TextField that when its picked a Path in the Main gets replaced (filepath) and just a simple OK Button that says: yeah run the main - because the main works perfectly it is just that I cant show that ui and I dont know how to really connect it to the Main.java
Any help is appreciated - Ty
Option 1
public class Launch extends Application {
public static Stage stage = null;
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Main.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
this.stage = stage;
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Option 2:
public class SidebarController implements Initializable {
#Override
public void initialize(URL url, ResourceBundle rb) {
}
#FXML
void btnHome_OnMouseClicked(MouseEvent event) throws IOException {
BorderPane borderPane = (BorderPane) ((Node) event.getSource()).getScene().getRoot();
Parent sidebar = FXMLLoader.load(getClass().getResource("/fxml/ContentArea.fxml"));
borderPane.setCenter(sidebar);
}
}
I am trying to create a stage in JavaFX that can pop up to the front in windows, while the main stage stays minimized.
This only works however, when the main stage is visible on the screen. I have tried making it work using Modality, but then the user can't interact with the main stage, which is not what i want.
The problem can be reproduced with the following Application:
public class MainApp extends Application {
#Override
public void start(Stage stage) throws Exception {
Scene mainScene = new Scene(new Parent() {});
Stage mainStage = new Stage();
mainStage.setScene(mainScene);
mainStage.show();
mainStage.setIconified(true);
Scene popUpScene = new Scene(new Parent() {});
Stage popUpStage = new Stage();
popUpStage.setScene(popUpScene);
Thread.sleep(5000);
popUp(popUpStage);
}
public static void popUp(Stage popUpStage){
if (popUpStage.isIconified()) popUpStage.setIconified(false);
popUpStage.show();
popUpStage.requestFocus();
popUpStage.toFront();
}
}
Is there anyone who has an answer to this problem?
Just add these two line to popUp. First line brings it to front. Second line allows interaction with the main stage or other windows.
popUpStage.setAlwaysOnTop(true);
popUpStage.setAlwaysOnTop(false);
I've been struggling for days over a stupid issue, and I need your help. I'm simply trying to display a indeterminate process indicator like the one below, within a separate Stage, while my main code performs a loop. If a certain condition is met while looping, the progress stage should close and the main code continues.
Right now I'm able to open a new stage prior to the main code starting the loop, but the progress indicator won't display in the stage yet. But once the condition is met, then the indicator suddenly appears in the stage and rotates. BUT as soon as the main code begins running again the indicator freezes, yet remains visible.
I'll briefly explain what each code below does. How do I make it so that the loading stage appears initially WITH the indicator visible, and then that stage CLOSES when the showMessage() method is called? Basically I want to show the user that background looping is happening, until the main code reaches showMessage().
Main.java
I won't post it, as it only creates the FIRST GUI. It uses menu.fxml as the resource in FXMLLoader ... and menu.fxml uses Controller.java as controller.
Controller.java
This code changes the scene in the Main GUI to have a new layout, which asks the user to click one of the visible buttons. It then checks if the source button is Button1... and if it is then create/show a new stage which uses sample.fxml as the resource. It then runs the final file, Loop.java
public class Controller implements Initializable {
public Stage loadingStage;
#FXML
ProgressIndicator progressIndicator;
public void handlerButtonClick() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("field.fxml"));
Parent rootOne = loader.load();
((Controller) loader.getController()).setPrimaryStage(primaryStage);
((Controller) loader.getController()).setPrimaryScene(scene);
sceneField = new Scene(rootOne, 420, 510);
primaryStage.setScene(sceneField);
primaryStage.setTitle("Import Data");
primaryStage.show();
}
public void fieldOption(ActionEvent e) throws Exception {
source = e.getSource();
if (source == Button1) {
Loop loopObj = new Main();
String[] args = {};
//Create new stage to contain the Progress Indicator
FXMLLoader loader2 = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root2 = loader2.load();
Controller2 controller = loader2.getController();
loadingStage = new Stage();
loadingStage.setTitle("Loading Stage");
Scene scene = new Scene(root2, 200, 200);
loadingStage.setScene(scene);
loadingStage.show();
//Runs the looper
loopObj.start(stage);
}
}
Sample.fxml
This code creates the Progress Indicator, and uses the controller Controller2
<AnchorPane prefHeight="200.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/2.2" fx:controller="Controller2">
<children>
<ProgressIndicator fx:id="progressIndicator" layoutX="78.0" layoutY="55.0" progress="-1.0"/>
</children>
</AnchorPane>
Controller2.java
This code implements Initializable in order to make the Progress Indicator visible:
public class Controller2 implements Initializable {
#Override
public void initialize(URL url, ResourceBundle rb) {
ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.setVisible(true);
}
}
Loop.java
This is the main code that performs the loop. The loading stage should display the Progress Indicator until Loop.java calls its showMessage() function, at which point the loading stage closes. I know that it seems that one of these methods aren't necessary, but its only because they're stripped for demo purposes.
public class Loop extends Application {
#Override
public void start(Stage ignored) throws Exception {
Platform.setImplicitExit(false);
for (int i = 0; i <= 100; i++) {
if (i == 75){
saveAttachment("Hello");
}
}
}
public static void saveAttachment(String subject) throws IOException, MessagingException {
showMessage(subject);
}
private static void showMessage(String subject) throws IOException, MessagingException {
// Close the loading stage here.
}
}
Notice how it uses public void start(Stage ignored). Main.java uses public void start(Stage primaryStage). Perhaps this is bad.
I'm so frustrated please help!
UPDATE
I've applied Brian's suggestions, and within Controller.java have added a new task like so:
Stage loadingStage = new Stage();
//create task object
Task<Void> task = new Task<Void>(){
#Override
protected Void call() throws Exception{
System.out.println("Background task started...");
FXMLLoader loader2 = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root2 = loader2.load();
Controller2 controller = loader2.getController();
loadingStage.setTitle("Hello World");
Scene scene = new Scene(root2, 450, 250);
System.out.println("HERE6");
loadingStage.setScene(scene);
System.out.println("HERE7");
loadingStage.show();
System.out.println("HERE8");
return null;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
System.out.println("Starting background task...");
th.start();
The problem now is that it doesn't reach the printout line saying "HERE7". It successfully enters the task and continues the main code, but the task doesn't get beyond where it prints "HERE6" therefore no new stage is opened. What gives?
In Ctlr2 you have ProgressIndicator progressIndicator = new ProgressIndicator(); but if it's in the FXML file you don't create a new one. Just do #FXML ProgressIndicator progressIndicator; like you have in the first Ctlr for some reason??
But this won't solve your problem, you're running everything on the JavaFX Application thread (GUI thread). What is probably happening (I didn't really read the code) is that you're doing both things on the same thread and the progress indicator has to wait for main to finish and then it can update the GUI. You put tags for task and concurrency, that's what you need.
Check here and do a search for more examples.
https://stackoverflow.com/a/22847734/2855515
I actually want to build a fx application that go from one fxml to another (with this process :fxml number 1 load then go with a click and another load).
I have made stage in my main class static and use it again and again.
according to Restriction of stack space in ram is it a good idea ?? or there is a better way?
these are parts of my code:
public class Main extends Application {
public static Stage stage;
#Override
public void start(Stage primaryStage) throws Exception{
stage=primaryStage;
Parent root = FXMLLoader.load(getClass().getResource("MainWidget.fxml"));
stage.setTitle("Welcome");
stage.setScene(new Scene(root, 300, 275));
stage.show();
}
and my controller is(loading another fxml!! )
public void someButtonController{
Parent root = FXMLLoader.load(getClass().getResource("/View/ShowWidget.fxml"));
Scene scene = new Scene(root,300,300);
Main.stage.setScene(scene);
Main.stage.show();}
I would not expose the Stage at all. Doing so couples your FXML-Controller pair to your Main class and prevents you ever using it without that class.
Instead, do something like
#FXML
private Button someButton ;
// ...
public void someButtonController{
Window window = someButton.getScene().getWindow();
if (window instanceof Stage) {
Parent root = FXMLLoader.load(getClass().getResource("/View/ShowWidget.fxml"));
Scene scene = new Scene(root,300,300);
Stage stage = (Stage) window ;
stage.setScene(scene);
stage.show(); // isn't it necessarily showing already?
}
}
Here I'm assuming that the controller is the controller for an FXML file representing Nodes that are displayed in the Stage you're trying to access. If that's not the case, you can still do something similar though it will be more complex.
The first stage that I load it always open properly as fullscreen.
stage.setFullScreen(true);
stage.setScene(login_scene);
But when I change to another FXML the applications stays fullscreen (no top toolbar.. ), but the actual view content gets resized on the prefWidth/prefHeight of the root AnchorPane from FXML (I can see the desktop in my bottom right corner :|), and I want it to be dynamic to my screen resolution.
Thanks.
#Later Edit:
So on the start method of my main Class I load a Scene (created from an FXML doc) and set it to the Stage (the start method param). I save this stage for later use.
When I press a button with the same stage I save previously I change the scene to another FXML document
#Screenshots:
http://tinypic.com/r/2079nqb/6 - 1st scene works normally - code from start override method of the main class
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
stage.setScene(new Scene(root));
stage.setFullScreen(true);
stage.show();
currentStage = stage;
}
http://tinypic.com/r/szfmgz/6 - after reloading the second scene - the code below from sample controller class
#FXML
private void handleButtonAction(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
JavaFXApplication12.currentStage.setScene(new Scene(root));
}
I have no idea about the real cause but here are 2 quick workarounds.
In the handleButtonAction method:
1) Don't create new scene just replace its content
#FXML
private void handleButtonAction(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
JavaFXApplication12.currentStage.getScene().setRoot(root);
}
2) If you really nead to create new scene then toggle fullscreen
#FXML
private void handleButtonAction(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
JavaFXApplication12.currentStage.setScene(new Scene(root));
Platform.runLater(new Runnable() {
#Override
public void run() {
JavaFXApplication12.currentStage.setFullScreen(false);
JavaFXApplication12.currentStage.setFullScreen(true);
}
});
}
If I am right to know your concern then You should use your primary stage as static or you can make it available to other controller by making getters and setters. So to get same stage to load other fxmls you can set it to when you are loading a fxml and also make sure that do not create another scene. Because due to new scene you actual content resized.
So you can use this
In Main.java:
YourController objYourController = loader.getController();
objYourController.setDialogStage(primaryStage);
In YourController.java:
public void setMystage(Stage primaryStage) {
this.primaryStage= primaryStage;
}
//To load another FXML
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
primaryStage.getScene().setRoot(rootLayout);
Hope it will help you.