Javafx程序,它可以由GUI和命令行控制?

Javafx程序,它可以由GUI和命令行控制?,java,javafx,command-line-interface,Java,Javafx,Command Line Interface,我正在使用javafxgui,但我也需要从命令行获得相同级别的功能。我想知道制作一个同时具有命令行和Javafx功能的主类的最佳方法是什么,这样您就可以在GUI上做一件事,然后在命令行上做下一件事。命令行还将更新GUI显示 我认为这是一个界限,不能太宽。其中一部分是:您的需求不明确。是否打算使用以下命令行: java -jar whatever.jar -command A java -jar whatever.jar -command B java -jar whatever.jar -com

我正在使用javafxgui,但我也需要从命令行获得相同级别的功能。我想知道制作一个同时具有命令行和Javafx功能的主类的最佳方法是什么,这样您就可以在GUI上做一件事,然后在命令行上做下一件事。命令行还将更新GUI显示

我认为这是一个界限,不能太宽。其中一部分是:您的需求不明确。是否打算使用以下命令行:

java -jar whatever.jar -command A
java -jar whatever.jar -command B
java -jar whatever.jar -command C
因此,您反复调用java,而which.jar基本上是一个客户机,它将连接到某个“服务器”来完成真正的工作,或者您设想的是什么

java -jar whatever.jar
> Type your command:
> A
... ran command A
> Type your command:
显然,这在这里有很大的不同

但最后,它还告诉我们解决方案在哪里:通过将这些客户机与实际执行分离

意思:你应该做两件事

  • 定义某些服务器必须提供的功能或服务
  • 然后,您可以研究创建使用这些服务的不同客户端的方法

避免将所有这些不同的方面烘焙到一个main()方法中

GUI上的所有内容都是基于事件的。这意味着当您按下按钮或以另一种方式与JavaFX窗口交互时,方法会被调用,就像在列表中选择一个项目一样

我建议将内部逻辑和GUI逻辑分开。 单击按钮时,调用链接到该按钮的handleButton(ActionEvent ActionEvent)方法。这个方法应该调用一个实际包含逻辑的其他类中的方法

您可以使用扫描仪通过命令行获取用户输入:

public String getUserInput() {
    Scanner scan = new Scanner(System.in);
    String s = scan.next();
    return s
}
您现在可以检查此用户输入字符串,并使用switch(s)语句连接相应的方法

我不确定您何时希望通过命令行获取此输入,但我建议在阶段中添加一个开发人员按钮,按下该按钮时调用
getUserInput()

(真的,这个问题是离题的,因为它太宽泛了。不过,对我来说,尝试一个对我来说似乎很自然的方法的概念证明已经足够有趣了,所以我还是回答了。)

这里基本上需要两件事:

  • 使用MVC方法,模型包含数据。您可以与命令行界面和UI共享同一个模型实例,因此两者都更新相同的数据。UI通常会观察模型并在数据更改时更新
  • 从JavaFX应用程序的
    start()
    方法启动CLI,在后台线程中运行它,这样它就不会阻塞UI。您只需要确保模型在正确的(即FX应用程序)线程上进行更新
  • 下面是一个简单的示例,它只计算整数列表的总数。这是一个模型,它存储列表和总数。它具有添加新值或清除列表的方法。请注意这些方法如何在UI线程上执行更改:

    import java.util.stream.Collectors;
    
    import javafx.application.Platform;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.ReadOnlyIntegerProperty;
    import javafx.beans.property.ReadOnlyIntegerWrapper;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    
    public class AddingModel {
    
        private final ObservableList<Integer> values = FXCollections.observableArrayList();
    
        private final ReadOnlyIntegerWrapper total = new ReadOnlyIntegerWrapper();
    
        public AddingModel() {
            total.bind(Bindings.createIntegerBinding(() -> 
                values.stream().collect(Collectors.summingInt(Integer::intValue)), 
                values));
        }
    
        private void ensureFXThread(Runnable action) {
            if (Platform.isFxApplicationThread()) {
                action.run();
            } else {
                Platform.runLater(action);
            }
        }
    
        public void clear() {
            ensureFXThread(values::clear);
        }
    
        public void addValue(int value) {
            ensureFXThread(() -> values.add(value));
        }
    
        public final ReadOnlyIntegerProperty totalProperty() {
            return this.total.getReadOnlyProperty();
        }
    
    
        public final int getTotal() {
            return this.totalProperty().get();
        }
    
    
        public ObservableList<Integer> getValues() {
            return values ;
        }
    }
    
    最后是组装所有这些的JavaFX应用程序。请注意,相同的模型实例同时传递给CLI和UI控制器,因此两者都在更新相同的数据。您可以在文本字段中输入一些值,然后在命令行中键入“show”,您将看到这些值。键入“clear”在命令行中,这些值将从UI中删除,等等

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    public class AddingApp extends Application {
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            AddingModel model = new AddingModel();
            AddingController controller = new AddingController(model);
    
            FXMLLoader loader = new FXMLLoader(AddingController.class.getResource("ValueTotaler.fxml"));
            loader.setControllerFactory(type -> {
                if (type == AddingController.class) {
                    return controller ;
                } else {
                    throw new IllegalArgumentException("Unexpected controller type: "+type);
                }
            });
            Parent root = loader.load();
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
    
            AddingCLI cli = new AddingCLI(model);
            Thread cliThread = new Thread(cli::processCommandLine);
            cliThread.setDaemon(true);
            cliThread.start();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    当然,您可以在不使用CLI的情况下创建UI,也可以在不使用UI的情况下创建CLI;两者都是相互独立的(它们都取决于模型)。

    scan.next()
    是一个阻塞调用:它会等待收到输入。因此,您的建议会使UI没有响应(完全冻结它)直到用户输入一些东西。
    import java.util.List;
    import java.util.Scanner;
    import java.util.regex.Pattern;
    
    public class AddingCLI {
    
        private final AddingModel model ;
    
        private final Pattern intPattern = Pattern.compile("-?[0-9]+");
    
        public AddingCLI(AddingModel model) {
            this.model = model ;
        }
    
        public void processCommandLine() {
            try (Scanner in = new Scanner(System.in)) {
                while (true) {
                    String input = in.next().trim().toLowerCase();
                    if (intPattern.matcher(input).matches()) {
                        int value = Integer.parseInt(input);
                        model.addValue(value);
                    } else if ("show".equals(input)) {
                        outputValues();
                    } else if ("clear".equals(input)) {
                        model.clear();
                        System.out.println("Values cleared");
                    } else if ("total".equals(input)) {
                        System.out.println("Total = "+model.getTotal());
                    }
                }
            }
        }
    
        private void outputValues() {
            List<Integer> values = model.getValues();
            if (values.isEmpty()) {
                System.out.println("No values");
            } else {
                values.forEach(System.out::println);
            }
        }
    }
    
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    public class AddingApp extends Application {
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            AddingModel model = new AddingModel();
            AddingController controller = new AddingController(model);
    
            FXMLLoader loader = new FXMLLoader(AddingController.class.getResource("ValueTotaler.fxml"));
            loader.setControllerFactory(type -> {
                if (type == AddingController.class) {
                    return controller ;
                } else {
                    throw new IllegalArgumentException("Unexpected controller type: "+type);
                }
            });
            Parent root = loader.load();
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
    
            AddingCLI cli = new AddingCLI(model);
            Thread cliThread = new Thread(cli::processCommandLine);
            cliThread.setDaemon(true);
            cliThread.start();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }