0

I have a program that uses CLI. The CLI uses a while loop to present the options and ask for input. and if I input "GUI" I need to call a simple GUI. I've made a simple GUI using JavaFX in a different class. So I used the .launch() method to open the GUI but after I do that the CLI stops. I have to close the GUI window to CLI to keep running. But if I do so I can't open the GUI again. I tried setting "Platform.setImplicitExit(false)" but when I do that even if I close the GUI the CLI won't continue. Is it possible to keep using the CLI while the GUI window is open and functioning? I started learning Java a few days ago so any advice is greatly appreciated!!! I'm using IntelliJ as the IDE

Edit: I am aware that I can't call launch() more than once that's why I need to keep using the CLI with the GUI window open and functioning. Is it not possible?

this is a sample of my main code

......
while(true){
    if (option.equals("GUI")){
        Application.launch(HelloApplication.class);
    }
    
}

and this is the HelloApplication.java (GUI class)

package com.example.demo1;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

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(), 720, 480);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }
}
  • 2
    You [can't call `launch()` more than once](https://openjfx.io/javadoc/20/javafx.graphics/javafx/application/Application.html#launch(java.lang.String...)) – James_D Jun 17 '23 at 15:43
  • @James_D Yes that's why I asked if I can keep using the CLI while the GUI window is open and functioning. So is it not possible? – Akila Indusara Jun 17 '23 at 15:45
  • 1
    Why not pass the arguments to [`launch`](https://openjfx.io/javadoc/17/javafx.graphics/javafx/application/Application.html#launch(java.lang.String...))? If so, please [edit] your question to include a [mre] that shows your revised approach. – trashgod Jun 17 '23 at 16:15
  • @trashgod Sorry I don't get your question. to my knowledge, I need arguments if I need to use them when launching the program right? currently, I don't need any. that's why I didn't pass any arguments. If I am missing something or if there're any arguments that can fix the issue, please explain if you can. Thanks. – Akila Indusara Jun 17 '23 at 16:54
  • The API I cited has an example. – trashgod Jun 17 '23 at 17:00
  • 1
    The call to `launch` blocks the calling thread until the JavaFX framework exits. You also cannot start the JavaFX framework more than once in a single JVM instance. Now, if you want to be able to keep the JavaFX framework running even when all windows are closed, then `setImplicitExit(false)` is definitely needed. And if you want to be able to open and close a window based on the CLI input, then you need to use either `Platform#startup(Runnable)` or `Platform#runLater(Runnable)`, depending on if the JavaFX framework is started already or not. – Slaw Jun 18 '23 at 01:15
  • 1
    You can still use `Application#launch(Class,String...)` instead of `Platform#startup(Runnable)` if you want (as it lets you continue to rely on the typical lifecycle of a JavaFX application). You'd simply need to call `launch` in a different thread than the one you use to process your CLI. All that said, it is atypical to mix a CLI application and a GUI application; you should typically prefer one or the other. Or have a command line argument "switch" that causes your application to use _either_ the CLI or the GUI, but not both at the same time. – Slaw Jun 18 '23 at 01:17
  • You're asking if you can avoid `launch()` blocking execution ("how to keep working after calling `launch()` method in java"). (Which you can, by putting it in a different thread.) What I was pointing out in my comment is that even if you solve that problem, you still have another problem because you can't call `launch()` more than once. You say you know that, but you obviously don't (or are just ignoring it), because you have the call to `launch()` in a loop. That issue is independent of the one you are asking. None of your code makes much sense. – James_D Jun 18 '23 at 03:48
  • The most straightforward solution is surely to call `launch()` once, from `main(...)`, in the usual way, and then start the CLI in a separate thread from the `start()` method. – James_D Jun 18 '23 at 03:52

1 Answers1

1

There are two major, and distinct, problems with your approach:

  1. The call to Application.launch(...) blocks execution until the JavaFX platform exits. From the documentation:

    The launch method does not return until the application has exited, either via a call to Platform.exit() or all of the application windows have been closed.

  2. If you call Application.launch() more than once, an exception will be thrown. Quoting the same documentation:

    It must not be called more than once or an exception will be thrown.

Thus your code won't work at all. Firstly, the "CLI" thread will be blocked the first time you call launch(), and it won't be able to do anything until the JavaFX runtime exits. You could avoid that blocking by calling launch() in a background thread, but then as soon as you tried to call launch() a second time, the call would fail with an exception.

It's not really clear what you're trying to achieve here. It would be highly unusual to have the same user interact with the same application both by a command line interpreter and via a graphical user interface.

If you really want to do this, I would approach it by starting the JavaFX application in the usual way (calling launch() once, from the main method, and using the start() method as your entry point). Then start the CLI from the start() method. Note that you still need to use a background thread, this time to run the CLI, because it is a blocking loop and running it on the FX Application Thread would prevent the UI from updating. Any GUI calls from the background thread have to be wrapped in Platform.runLater(...) to ensure they are called on the FX Application Thread.

Also note that you almost certainly need to call Platform.setImplcitExit(false) to prevent the FX runtime from closing when you close the first window.

Again, this doesn't really make any sense at all from a User Experience perspective, but here is a quick example implementing this:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
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.VBox;
import javafx.stage.Stage;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Platform.setImplicitExit(false);
        Thread cliThread = new Thread(this::runCLI);
        cliThread.start();
    }

    private void runCLI() {
        try (Scanner scanner = new Scanner(System.in)) {
            boolean exit = false ;
            while (! exit) {
                System.out.println("Enter option (GUI to show gui, Quit to exit):");
                String option = scanner.nextLine();
                switch(option.toLowerCase()) {
                    case "gui":
                        Platform.runLater(this::showGUI);
                        break;
                    case "quit":
                        exit = true;
                        break;
                    default:
                        System.out.println("Other action: "+option);
                }
            }
            Platform.exit();
        }
    }

    private void showGUI() {
        Stage stage = new Stage();
        Label label = new Label("Here is a GUI Window");
        Button button = new Button("Close");
        button.setOnAction(e -> stage.hide());
        VBox root = new VBox(10, label, button);
        root.setPadding(new Insets(40));
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
        stage.toFront();
    }

    public static void main(String[] args) {
        Application.launch();
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • thank you so much!!!! sorry about my comment earlier I didn't explain it well there's a post on how to call launch again or something similar to that here [link](https://stackoverflow.com/a/24320562/15515954) So when tried that got the problem Platform.setImplcitExit(false) blocking the loop. And yes this is really stupid this is an assignment I got and I have no idea why they need me to do it in both CLI and GUI at the same time. – Akila Indusara Jun 19 '23 at 01:16