[英]Javafx program which can be controlled both by GUI and Command Line?
我正在使用Javafx GUI,但我也需要從命令行獲得相同級別的功能。 我想知道使主類同時具有命令行和Javafx功能的最佳方法是什么,以便您可以在GUI上做一件事,然后在命令行上做下一件事。 命令行也會更新GUI顯示。
(確實,這個問題太籠統了,因為它太寬泛了。盡管如此,對於我來說,嘗試嘗試一種對我來說似乎很自然的方法的概念驗證還是很有趣的,所以我還是回答了。)
您基本上需要在這里做兩件事:
start()
方法啟動CLI,並在后台線程中運行它,以免阻塞UI。 您只需要確保該模型在正確的線程(即FX Application)上進行更新即可。 這是一個簡單的示例,它僅計算整數列表的總數。 這是模型,它存儲列表和總數。 它具有添加新值或清除列表的方法。 注意這些方法如何在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 ;
}
}
這是UI代碼。 首先是FXML中的視圖:
<?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>
和一個控制器,它觀察並更新模型:
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控制器,因此兩者都在更新相同的數據。 您可以在文本字段中輸入一些值,然后在命令行中輸入“ 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:
顯然,這在這里有很大的不同。
但是最后,它還告訴我們解決方案在哪里:通過將這些客戶端與實際執行脫鈎。
含義:您應該做兩件事
避免將所有這些不同方面烘焙到一個main()方法中!
GUI上的所有內容都是基於事件的。 這意味着,當您按下按鈕或以其他方式(例如,選擇列表中的項目)與JavaFX Window交互時,將調用方法。
我建議您將內部邏輯和GUI邏輯分開。 單擊按鈕時,調用鏈接到該按鈕的handleButton(ActionEvent actionEvent)方法。 此方法應在您實際包含邏輯的其他類之一中調用一個方法。
您可以使用掃描儀通過命令行獲取用戶輸入:
public String getUserInput() {
Scanner scan = new Scanner(System.in);
String s = scan.next();
return s
}
現在,您可以檢查此用戶輸入字符串,並將相應的方法與switch(s)語句連接。
我不確定何時要通過命令行獲取此輸入,但是我建議在舞台上添加一個開發人員按鈕,當按下該按鈕時會調用getUserInput()
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.