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

2024-04-13

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


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

你在这里基本上需要两件事:

  1. 使用 MVC 方法,模型包含数据。您可以与命令行界面和 UI 共享相同的模型实例,因此两者都会更新相同的数据。像往常一样,UI 将观察模型并在数据发生变化时进行更新。
  2. 从 JavaFX 应用程序的启动 CLIstart()方法,在后台线程中运行它,这样就不会阻塞 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 中的视图:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Javafx程序可以通过GUI和命令行控制吗? 的相关文章

随机推荐

  • 如何从片段中的 RecyclerView 适配器启动活动[重复]

    这个问题在这里已经有答案了 我无法点击RecyclerView到一个新的Activity from RecyclerViewAdapter I call ItemClick here DayAdapter java holder setIt
  • Java 中有可用的 SMS Pdu 解析器吗?

    任何人都知道 Java 中可用的 Pdu 解析器来自 byte 数组 我主要关心的是获得符合 GSM 标准的用户数据头 UDH 我的意思是正确地得到它 smsLib http smslib org 已经比较成熟了 您还可以使用 Androi
  • 对于文件中的每一行,将行写入Python中的单个文件

    我有一个文本文件 需要将其逐行分隔成单独的文本文件 因此 如果主文件包含字符串 foo bar bla 我将有 3 个文件 可以用数字命名为 1 txt 包含字符串 foo 2 txt 包含字符串 bar 和 3 txt 包含字符串 bla
  • 使用 PHP-SDK 的 Facebook 身份验证

    我已经尝试了几天了 但没有成功 我设法使用 PHP SDK 附带的 example php 并且工作得很好 我只需要存储返回的会话并稍后使用它 这样我就可以访问而无需重新验证 我尝试将会话存储在数据库中的序列化字段中 然后恢复数据 反序列化
  • 通过Javascript在浏览器中获取Android版本

    我正在构建一个 Web 应用程序 并希望在 3 0 版本以下的 Android 设备上禁用过渡效果 有没有办法在浏览器中通过Javascript获取Android版本号 如果是这样怎么办 function getAndroidVersion
  • 解码 PDF 文档中文本的 FlateDecoded 部分

    Using peepdf https code google com p peepdf 我正在分析两个简单的 pdf 文件 这两个文件都包含单行文本 ZYXWVUTSRQQRSTUVWXYZ 并且都是在 Mac OS X 上创建的 第一个文
  • R中不等长度列表的平均值

    我有不等长度的向量列表 我想要的是平均值 在每个索引处 例如 a c 2 3 4 b c 2 3 4 5 c c 5 0 3 4 6 avg a b c c 9 3 6 3 11 3 9 2 6 1 如何在 R 中实现这一点 我们可以将向量
  • 如何在 Java Swing 中自动滚动到底部

    我有一个简单的 JPanel 上面有一个 JScrollPane 根据需要带有垂直滚动条 东西被添加到 JPanel 或从中删除 当它超出面板底部时 我希望 JScrollPane 根据需要自动向下滚动到底部 或者如果某些组件离开面板则向上
  • javascript focus() 在 Firefox 和 IE 上不起作用?

    我正在尝试显示一个表单并聚焦它 由于某种原因它只适用于 Chrome 我怎样才能让它跨浏览器工作 div div
  • 客户端验证不会针对 CompareAttribute DataAnnotation 触发

    我正在布置一个比较两个密码字符串的视图 我的模型之一的两个属性非常简单 Required RegularExpression S ErrorMessage White space is not allowed StringLength 20
  • 方法和内部类同名(错误:...与先前的声明冲突)

    我打算有一个类 它有一个内部类和一个名称相似的方法 的代码为example1 cpp即使我有一个内部类和一个同名的方法 编译也没有问题B While example2 cpp如果我重命名将不起作用Position to position用小
  • Kotlin - 在 Android 中转换 Singleton DatabaseController 的最佳方式

    我正在通过 Kotlin in Action 学习 Kotlin 并且正在慢慢地将 Android 应用程序代码转换为它 但我在转换下面的类时发现了一些问题 public class DatabaseController private s
  • VueJS:使用对象文字与返回对象的函数定义“数据”

    定义之间有什么区别data对象可以通过以下方式 1 使用对象字面量 data title Helly VueJS 2 函数返回对象 data return title Helly VueJS 来自文档 https v2 vuejs org
  • 如何使用 pyinstaller 创建最小大小的可执行文件?

    我使用的是 Windows 10 安装了 anaconda 但我想使用 python 3 5 在一个新的 干净的最小环境中独立创建一个可执行文件 所以我做了一些测试 测试1 我在文件夹 testenv 中创建了一个 python 脚本 te
  • 在mysql中选择两个独立的表[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 results mysqli gt query SELECT product name price FROM herbs an WHERE
  • 双倍的 Pow 实现

    我正在开发一个用于运动控制的代码 但我遇到了 pow 函数的问题 我使用VS2010作为IDE 这是我的问题 我有 double p 100 0000 double d 1000 0000 t1 pow p 8 0000 d 1 00 4
  • YouTube.Builder 的正确使用方法

    我需要获取用户 YouTube 视频 这是我的代码 仅尝试获取用户 YouTube 频道 但它不起作用 选择帐户后 加载Youtube频道总是抛出错误 我读了这个answer https stackoverflow com a 214077
  • 如何使用类库中的控制器?

    我在类库中有控制器 但我无法弄清楚如何让主项目识别它们 主项目引用了我的类库 我需要在某处注册它们吗 我想同时使用控制器和 ApiController EDIT 路线配置 创建项目后未更改 public class RouteConfig
  • 在 swift 中将 UIImage 转换为 base64 字符串

    我正在尝试将 UIImage 转换为 base64 字符串 目的是将其上传到后端服务器 然而 我在这篇文章中找到的转换代码 应该是Apple自己的实现 生成了一个无效的字符串 UIImage 和 Base64 字符串之间的转换 https
  • Javafx程序可以通过GUI和命令行控制吗?

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