Javafx程序可以通过GUI和命令行控制吗?

我正在使用Javafx GUI,但我还需要命令行提供相同级别的function。 我想知道什么是最好的方法来创建一个同时具有命令行和Javafxfunction的主类,这样你就可以在GUI上做一件事,然后在命令行上做下一件事。 命令行还将更新GUI显示。

(真的,这个问题是偏离主题的,因为它太宽泛了。但是,对我来说,尝试对我来说看起来很自然的方法的概念certificate非常有趣,所以无论如何我都回答了​​。)

你基本上需要两件事:

  1. 使用MVC方法,包含数据的模型。 您可以使用命令行界面和UI共享相同的模型实例,因此两者都会更新相同的数据。 像往常一样,UI将观察模型并更新数据是否发生变化。
  2. 从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 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 getValues() { return values ; } } 

这是UI代码。 首先是FXML中的一个视图:

               

和一个观察和更新模型的控制器:

 import java.util.function.UnaryOperator; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.TextField; import javafx.scene.control.TextFormatter; public class AddingController { private final AddingModel model ; @FXML private TextField valueField ; @FXML private ListView values ; @FXML private Label sum ; public AddingController(AddingModel model) { this.model = model ; } @FXML private void initialize() { values.setItems(model.getValues()); sum.textProperty().bind(model.totalProperty().asString("Total = %,d")); // Allow only integer values in the text field: UnaryOperator filter = c -> { if (c.getControlNewText().matches("-?[0-9]*")) { return c; } else { return null ; } }; valueField.setTextFormatter(new TextFormatter<>(filter)); } @FXML private void addValue() { String text = valueField.getText(); if (! text.isEmpty()) { int value = Integer.parseInt(text); model.addValue(value); valueField.clear(); } } @FXML private void clearValues() { model.clear(); } } 

现在是一个简单的命令行解释器,它从命令行读取并引用模型。 它支持整数条目(为模型添加值)或命令totalshowclear

 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 values = model.getValues(); if (values.isEmpty()) { System.out.println("No values"); } else { values.forEach(System.out::println); } } } 

最后,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; 两者都是相互独立的(它们都只依赖于模型)。

我认为这是过于宽泛的边缘 。 其中一部分是:您的要求不清楚 。 您打算使用命令行,如:

 java -jar whatever.jar -command A java -jar whatever.jar -command B java -jar whatever.jar -command C 

所以 – 你反复调用java,而且what.jar基本上是一个客户端,它接受一些“服务器”来完成真正的工作,或者你是否设想

 java -jar whatever.jar > Type your command: > A ... ran command A > Type your command: 

显然,这在这里有很大的不同。

但最后,它还告诉我们解决方案的位置:通过将这些客户端从实际执行中解耦。

含义:你应该做两件事

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

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

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

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

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

 public String getUserInput() { Scanner scan = new Scanner(System.in); String s = scan.next(); return s } 

您现在可以检查此用户输入字符串,并使用switch(s)语句连接相应的方法。

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