簡體   English   中英

可以同時通過GUI和命令行控制的Javafx程序?

[英]Javafx program which can be controlled both by GUI and Command Line?

我正在使用Javafx GUI,但我也需要從命令行獲得相同級別的功能。 我想知道使主類同時具有命令行和Javafx功能的最佳方法是什么,以便您可以在GUI上做一件事,然后在命令行上做下一件事。 命令行也會更新GUI顯示。

(確實,這個問題太籠統了,因為它太寬泛了。盡管如此,對於我來說,嘗試嘗試一種對我來說似乎很自然的方法的概念驗證還是很有趣的,所以我還是回答了。)

您基本上需要在這里做兩件事:

  1. 使用MVC方法,其中模型包含數據。 您可以與命令行界面和UI共享同一模型實例,因此兩者都更新相同的數據。 與往常一樣,UI將觀察模型並在數據更改時進行更新。
  2. 從JavaFX應用程序的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();
    }
}

現在是一個簡單的命令行解釋器,它從命令行讀取並引用模型。 它支持整數輸入(向模型添加值),或命令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<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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM