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

标签 java javafx command-line-interface

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

最佳答案

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

你基本上需要两件事:

  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<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();
    }
}

现在是一个简单的命令行解释器,它从命令行读取并引用模型。它支持整数输入(向模型添加值)或命令 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 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);
    }
}

enter image description here

当然,您可以只创建不带 CLI 的 UI,或者只创建不带 UI 的 CLI;两者相互独立(它们都取决于模型)。

关于可以通过 GUI 和命令行控制的 Javafx 程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49675539/

相关文章:

javafx - 如何以编程方式强制JavaFX应用程序关闭请求

JavaFX 标签导致异常

linux - Cron 作业未执行预期任务

java - 如何从 Java 中的重写方法传播异常

java - JMS 1.1 : Load sharing client side for Point-to-Point mode

java - 检查文件是否存在java

java - 从 ListView 中获取选定的行号

php - PHP 如何取消 RabbitMQ 中的消费者?

c - 将数据与用户输入分开。

java - RestController 在 oauth2 Spring Boot 中不起作用