我正在使用 Javafx GUI,但我也需要来自命令行的相同级别的功能。我想知道制作同时具有命令行和 Javafx 功能的主类的最佳方法是什么,这样您就可以在 GUI 上做一件事,然后在命令行上做下一件事。命令行也会更新 GUI 显示。
最佳答案
(真的,这个问题是题外话,因为它太宽泛了。不过,这对我来说很有趣,可以尝试证明对我来说似乎很自然的方法的概念,所以我还是回答了它。)
你基本上需要两件事:
- 使用 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 ;
}
}
这是用户界面代码。首先是 FXML 格式的 View :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="AddingController">
<top>
<HBox spacing="5">
<TextField fx:id="valueField" onAction="#addValue"/>
<Button text="Clear" onAction="#clearValues"/>
</HBox>
</top>
<center>
<ListView fx:id="values"/>
</center>
<bottom>
<Label fx:id="sum"/>
</bottom>
</BorderPane>
和一个 Controller ,它观察和更新模型:
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<Integer> 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<TextFormatter.Change> 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();
}
}
现在是一个简单的命令行解释器,它从命令行读取并引用模型。它支持整数输入(向模型添加值)或命令 total
、show
或 clear
:
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);
}
}
}
最后,组装所有这些的 JavaFX 应用程序。请注意,相同的模型实例会同时传递给 CLI 和 UI Controller ,因此两者都在更新相同的数据。您可以在文本字段中输入一些值,然后在命令行中键入“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;两者相互独立(它们都取决于模型)。
关于可以通过 GUI 和命令行控制的 Javafx 程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49675539/