I'm doing a project, where I have to switch scenes depending on user input. At the moment I have a Gui-Class, which is an Application and loads the first fxml-File, it is launched by a MainController-class:
public class MainController {
public MainController() {
Application.launch(Gui.class);
}
}
The Gui-Class:
public class Gui extends Application{
Stage primaryStage;
//I wanna ideally use this method to switch scenes
public void switchen(String nextScene) throws Exception
{
Parent nextS;
if(nextScene.equals("singleMenue"))
{
nextS = FXMLLoader.load(getClass().getResource("SingleMenue.fxml"));
primaryStage.hide();
primaryStage.setScene(new Scene(nextS, 900, 600));
primaryStage.show()
}
}
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
Parent decisionScene = FXMLLoader.load(getClass().getResource("Decision.fxml"));
primaryStage.setTitle("title");
primaryStage.setScene(new Scene(decisionScene, 900, 600));
primaryStage.show();
}
I also have a different Controller-class for each .fxml-file (this is the first):
public class DecisionController {
#FXML
private Button singleButton;
#FXML
private Button multiButton;
public DecisionController() {
}
#FXML
private void buttonPressed(ActionEvent event) throws IOException
{
if(event.getSource() == singleButton)
{
System.out.println("single");
// I wanna somehow call switch("singleMenue"); here
}
else if(event.getSource() == multiButton)
{
System.out.println("multi");
}
}
}
So how do I switch the Scenes from inside the Controller now ?
I tried to generally go with the MVC-pattern but I don't know how it should be possible for my DecisionController-Object to call a method inside the Gui-Application. I would need to reach inside a running Thread here, right ?
I also don't know how I could turn over my Gui-Instance to my DecisionController, since the latter one is initialized from the Decision.fxml.
One other thing I tried was to switch the Scene directly from the DecisionController with:
#FXML
private void buttonPressed(ActionEvent event) throws IOException
{
if(event.getSource() == singleButton)
{
System.out.println("single");
Stage mainstage = (Stage) singleButton.getScene().getWindow();
Parent singleScene = FXMLLoader.load(getClass().getResource("SingleMenue.fxml"));
mainstage.setScene(new Scene(singleScene, 900, 600));
}
else if(event.getSource() == multiButton)
{
System.out.println("multi");
}
}
This understandably doesn't work either since manipulation from outside the application thread isn't possible.
I'm kind of new to JavaFX & FXML so my general idea for the structure of the program might just be totally wrong, maybe this isn't how you MVC. Any feedback is much appreciated :)
Related
SO, I have a JavaFX application with an already implemented Tray that needs 1 option to reopen the window/stage minimized by this GuiController function, which is called by the fxml:
`public void minimizeImageViewClick(MouseEvent mouseEvent) {
if (mouseEvent.getSource() == minimizeImageView) {
stage = (Stage) ((ImageView) mouseEvent.getSource()).getScene().getWindow();
stage.setIconified(true);
}
if(mouseEvent.getSource() == minimizePane){
stage = (Stage) ((Pane) mouseEvent.getSource()).getScene().getWindow();
stage.setIconified(true);
}
}`
There is also one to maximize, implemented by the same means:
public void maximizeImageViewClick(MouseEvent mouseEvent) {
if (mouseEvent.getSource() == maximizeImageView) {
if (ExamAlertStateController.getInstance().getWindowSizeState() == WindowSizeState.INIT) {
stage = (Stage) ((ImageView) mouseEvent.getSource()).getScene().getWindow();
stage.setMaximized(true);
update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.MAXIMIZED);
} else {
stage = (Stage) ((ImageView) mouseEvent.getSource()).getScene().getWindow();
stage.setMaximized(false);
update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.INIT);
}
}
}`
A piece of the Tray, with the Open (Abrir) option I need to implement and another which only change the screen and the state, as all the other after that:
# SpringBootApplication
public class Application {
//private final MenuItem menuItemSelectedFile;
//Stage stage;
private SystemTray systemTray;
public static final URL MULTI_LOGO = Application.class.getResource("source.png");
private Timer evaluatorTimer;
public static void main(String[] args) {
* code *
}
public Application() {
Log.i(this.getClass().getName(),"Text");
SystemTray.FORCE_GTK2=true;
SystemTray.DEBUG = true;
CacheUtil.clear();
this.systemTray = SystemTray.get();
if (systemTray == null) {
throw new RuntimeException("text!");
}
systemTray.setTooltip("text");
systemTray.setImage(MULTI_LOGO);
systemTray.setStatus("TEXT");
Menu mainMenu = systemTray.getMenu();
MenuItem openMenuItem = new MenuItem("Abrir", new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
}
});
openMenuItem.setTooltip("Abrir programa");
mainMenu.add(openMenuItem);
mainMenu.add(new Separator());
MenuItem loginMenuItem = new MenuItem("Login", new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
ExamAlertStateController.getInstance().setWindowState(WindowState.LOGIN);
}
});
loginMenuItem.setTooltip("Login Text");
mainMenu.add(loginMenuItem);
mainMenu.add(new Separator());
The stage creation in the View file:
public class eaView extends Application {
private static final long ONE_SECOND = 1000;
private static double xOffset = 0;
private static double yOffset = 0;
private boolean ignore;
private Timer updaterTimer;
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader();
URL urlLoadingScreen = getClass().getResource("/fxml/source.fxml");
loader.setLocation(urlLoadingScreen);
GridPane gridLoadingScreen = loader.load();
Scene scene = new Scene(gridLoadingScreen);
stage.initStyle(StageStyle.UTILITY);
stage.setOpacity(0);
stage.setHeight(0);
stage.setWidth(0);
stage.show();
Stage mainStage = new Stage();
//stage.setResizable(false);
//mainStage.setResizable(false);
mainStage.initOwner(stage);
mainStage.initStyle(StageStyle.UNDECORATED);
mainStage.setScene(scene);
mainStage.show();
So, I wasn't able to to call the maximizeImageViewClick function to it - I can by making it static, but it calls an update() method which contains a lot of FXML objects which cannot be altered, couldn't found the proper stage way of doing it (the attempts:
stage.setScene(scene);
stage.setMaxHeight(0);
stage.setMaxWidth(0);
stage.setOpacity(0);
stage.setIconified(true);
//ExamAlertView.
System.out.println("foi");
//ExamAlertView.
//System.out.println(stage.getProperties());
stage.show();
//if (ExamAlertStateController.getInstance().getWindowSizeState() == WindowSizeState.INIT) {
//stage = (Stage) ((ImageView) e.getSource()).getScene().getWindow();
//stage.setIconified(false);
//stage.setMaximized(true);
//stage.show();
//ImageView
//ExamAlertGuiController.setMinimizeImageView(ExamAlertGuiController.maximizeImageView);
System.out.println("foi2");
//ExamAlertGuiController.update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.MAXIMIZED);
/* } else {
stage = (Stage) ((ImageView) e.getSource()).getScene().getWindow();
stage.setMaximized(false);
stage.setIconified(false);
//ExamAlertGuiController.update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.INIT);
}*/
//System.out.println(stage.isIconified());
) which was only possible by making
public static Stage stage;
public static Scene scene;
in the GuiController file.
Really out of ideas here. Already did read the javafx.stage class the best I could.
Maybe I'm trying to access the wrong stage - stage UTILITY to remove taskbar button, mainStage UNDECORATED is the real deal, yet can't reach it - but I dont know....
How do I do this?
Thanks in advance.
My question is pretty straight forward (I'm just a recreational coder playing around with stuff). I have established a connection to an API and via the magic of RxJava I can subscribe to the json data emitted the API . I have also created a very basic GUI using FXML.
How do I make the text within TextArea = textAreaA update every time an event is emitted via subscription? Said differently, how do I make the TextArea display the API feed?
I've read the RxJavaFx-guide from cover to cover and it seems to focus more on the Fx to Rx directional flow of events :(
Here is a sample of my code:
public class Tester extends Application{
private static final Logger LOG = LoggerFactory.getLogger(Tester.class);
public static void main(String[] args) throws IOException, InterruptedException{
launch(args);
}
public void start (Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
primaryStage.setTitle("App Window");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
TESTER_API.TESTER_API_Connector();
}
}
public class TESTER_API {
public static void TESTER_API_Connector() throws IOException, InterruptedException {
StreamingEvents emissions = StreamingEventsFactory.INSTANCE.createEvents(TheseStreamingEvents.class.getName());
emissions.connect().blockingAwait();
emissions.getEvents().getSomethingSpecific()
.subscribe(event -> System.out.println(event.getSomethingSpecific()) // Here is the event that I would like to bind or otherwise push to textAreaA in the FXML controller
,throwable -> new Exception("GUI error!"));
}
}
public class FXMLDocumentController implements Initializable {
#FXML
public TextArea textAreaA;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
}
}
My idea was to make TESTER_API call a function in FXMLDocumentController that updates textAreaA. I would recommend using Platform.runLater() to enshure thread safety.
public class Tester extends Application{
[...]
public void start (Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
FXMLDocumentController controller = loader.getController();
primaryStage.setTitle("App Window");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
TESTER_API.TESTER_API_Connector(controller);
}
}
public class TESTER_API {
public static void TESTER_API_Connector(FXMLDocumentController controller) throws IOException, InterruptedException {
[...]
emissions.getEvents().getSomethingSpecific()
.subscribe(event -> Platform.runLater(() -> {
controller.updateTextArea(event.getSomethingSpecific());
}),
throwable -> new Exception("GUI error!"));
}
}
public class FXMLDocumentController implements Initializable {
#FXML
public TextArea textAreaA;
[...]
public void updateTextArea(String string) {
textAreaA.setText(string);
}
}
I am fairly new to JAVAFX so please bear with me. Any help is greatly appreciated. Firstly, i have a Main app that loads addItems.fxml and addItemsController.
public class UncookedApp extends Application {
private Stage stage;
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
try {
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addItems.fxml"));
AnchorPane root=loader.load();
addItemsController itemsCtrl=loader.getController();
itemsCtrl.setMainApp(this);
Scene scene=new Scene(root,1024,768);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e){
e.printStackTrace();
}
}
public Stage getStage() {
return stage;
}
public static void main(String[] args) {
launch(args);
}
}
This is part of the addItemsController:
private UncookedApp mainApp;
public UncookedApp getMainApp() {
return mainApp;
}
public void setMainApp(UncookedApp mainApp) {
this.mainApp = mainApp;
}
public void toMeat(ActionEvent event) {
Stage stage1=mainApp.getStage();
try {
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addMeat.fxml"));
Parent root=loader.load();
addMeatCtrl itemsCtrl=loader.getController();
// itemsCtrl.setMainApp(this);
Scene scene=new Scene(root,1024,768);
stage1.setScene(scene);
stage1.show();
} catch(Exception e){
e.printStackTrace();
}
}
The addItemsController will load a different page, addMeat. I got that to work but when I try to go back from addMeat to addItems with a button handle, it doesn't work. Is it something to do with retrieving the mainApp's stage? How can I solve this?
This is what I've tried.
public void handleBackFromMeat(ActionEvent event) {
try{
Stage stage=mainApp.getStage();
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addItems.fxml"));
Parent root=loader.load();
addItemsController itemsCtrl=loader.getController();
Scene scene=new Scene(root,1024,768);
stage.setScene(scene);
stage.show();
}catch (Exception e){
e.printStackTrace();
}
}
I also had posted a similar question and then i went ahead with my way
I need to create a back button functionality in javafx?
What you can do as i did for my application was make two static list at the start of the application and then fill it with the Stage and the Fxml page name if you are using fxml and then have a central back button method on each page to move the previous page by iterating on the last item added to the list .
Two static lists in the main class
public static List<Stage>stageval = new ArrayList<Stage>();
public static List<String>fxmlval = new ArrayList<String>();
When ever i switch my fxml view i added the stage and the fxml file name to their respective lists.
fxmlval.add("Instance.fxml");
stage = (Stage)validate.getScene().getWindow();
stageval.add((Stage) delete.getScene().getWindow());
And a central back button method when ever the user clicked the back button i iterated to last item of both the lists and then after changing the stage deleted the values from the list .
public class BackButton {
public void back(String instance,Stage stage) throws IOException{
Parent parent = FXMLLoader.load(getClass().getResource(instance));
Scene scene = new Scene(parent);
scene.getStylesheets().add("/css/Style.css");
stage.setResizable(false);
stage.setScene(scene);
stage.show();
}
}
#FXML
public void back() throws IOException {
BackButton bb = new BackButton();
bb.back(fxmlval.get(fxmlval.size() - 1),
stageval.get(stageval.size() - 1));
fxmlval.remove(fxmlval.size() - 1);
stageval.remove(stageval.size() - 1);
}
Hope this helps its a work around but the solution works.
At the Base I have an AnchorPane then a SplitPane. On the left pane I have a listView and depending on the list element selected, the right pane displays the appropriate content. The way I have done this is by overlapping AnchorPanes and setting them to .setVisible(false) initially and as they are selected I set them to .setVisible(true) like so :
public void listSelection() {
String selection = listView.getSelectionModel().getSelectedItem();
switch(selection) {
case "Speed of sound":
disableOld(); // disables old AnchorePane
response.setText("Speed of sound conversion");
AnchorPane1.setVisible(true);
break;
case "Temperature conversion":
disableOld();
response.setText("Temperature conversion");
AnchorPane2.setVisible(true);
break;
}
}
I would like to know how to produce the same effect visually but with different scenes as I would like for each new AnchorPane to have it's own FXML and ControllerClass.
You can implement something like this :
Your main class :
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Title");
primaryStage.setScene(createScene(loadMainPane("path_of_your_fxml")));
primaryStage.show();
}
private Pane loadMainPane(String path) throws IOException {
FXMLLoader loader = new FXMLLoader();
Pane mainPane = (Pane) loader.load(
getClass().getResourceAsStream(path));
return mainPane;
}
private Scene createScene(Pane mainPane) {
Scene scene = new Scene(mainPane);
return scene;
}
public static void main(String[] args) {launch(args); }
Then you can create a separate class call Navigator to store all your fxml paths:
public class Navigator {
private final String P1;
private final String P2;
//then you can implement getters...
public String getP1() {
return P1;
}
public String getP2() {
return p2;
}
private static FxmlController Controller;
public static void loadPane(String fxml) {
try {
FxmlController.setPane(
(Node) FXMLLoader.load(Navigator.class.getResource(fxml)));
} catch (IOException e) {
e.printStackTrace();
}
}
public Navigator() throws IOException {
this.P1 = "p1.fxml";
this.P2 = "p2.fxml";}
In your main FxmlController(which is the controller of the permanent layer of your application , rest of the stack-panes-{p1 and p2} will load on your permanent layer )
This is how you load layers on the main FxmlController :
#FXML
private StackPane stackPaneHolder;
...
public void setPane(Node node) {
if (stackPaneHolder.getChildren().isEmpty()) {
//if stackPaneHolder is empty
stackPaneHolder.getChildren().add(node);
} else {
if (stackPaneHolder.getClip() != node) {
//if stackPaneHolder is not empty then remove existing layer and add new layer
stackPaneHolder.getChildren().remove(0);
stackPaneHolder.getChildren().add(0, node);
}
}
}
Then you can load panes by pressing a button like below :
#FXML
private void btnAction(ActionEvent event) throws IOException {
Navigator.load(new Navigator().getP1());
..
This is how it works :
So I am trying to handle WINDOW_SHOWN event from my controller with code like this:
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
initializeDatePickers();
System.out.println("payer number in initialize: " + payerNumber);
URL location = getClass().getResource("/createUser.fxml");
FXMLLoader loader = new FXMLLoader();
try {
Parent root = (Parent) loader.load(location.openStream());
root.getScene().getWindow().setOnShown(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
System.out.println("ONSHOWN");
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
But all I've got was endless cycle and program crash.
The code below didn't work either, it returns NullPointerException:
#FXML private AnchorPane createUserDialog; //my root pane
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
createUserDialog.getScene().getWindow().addEventHandler(WindowEvent.WINDOW_SHOWN,
new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent window) {
System.out.println("ONSHOWN");
}
});
}
Implementing WindowEvent interface didn't work at all, don't know why.
So, how could I handle this event? And why I've got NullPointerException? In docs said that initialize() calling only after root pane completely processed.
When the initialize() method is being executed, the root pane is completely constructed but is not added to a scene, or a window. (The initialize() method is executed as part of the execution of your FXMLLoader's load() method; check the code where you call that and you will see that you add the root to a scene and place it in a window after that.) So during the execution of intialize(), root.getScene() will return null.
You can use a Binding to check when the window changes and attach a listener to it:
final EventHandler<WindowEvent> shownHandler = new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
System.out.println("Shown");
}
};
Bindings.<Window>select(createUserDialog.sceneProperty(), "window").addListener(new ChangeListener<Window>() {
#Override
public void changed(ObservableValue<? extends Window> observable,
Window oldValue, Window newValue) {
if (oldValue != null) {
oldValue.removeEventHandler(WindowEvent.WINDOW_SHOWN, shownHandler);
}
if (newValue != null) {
newValue.addEventHandler(WindowEvent.WINDOW_SHOWN, shownHandler);
}
}
});
This code assumes the root is only ever added to one window; in the unlikely event you're taking the root out of one window and putting it in another during your application life cycle, you would need to remove the listener from the old window. If you need this I'll update the code, but it makes it more complex.