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

时间:2018-04-05 14:58:56

标签: java javafx command-line-interface

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

3 个答案:

答案 0 :(得分:3)

(真的,这个问题是偏离主题的,因为它太宽泛了。但是,对我来说,尝试对我来说看起来很自然的方法的概念证明非常有趣,所以无论如何我都回答了​​。)

你基本上需要两件事:

  1. 使用MVC方法,包含数据的模型。您可以使用命令行界面和UI共享相同的模型实例,因此两者都会更新相同的数据。与往常一样,用户界面将观察模型,并在数据发生变化时进行更新。
  2. 从JavaFX应用程序的start()方法启动CLI,在后台线程中运行它,以便它不会阻止UI。您只需要确保模型在正确的(即FX应用程序)线程上进行更新。
  3. 这是一个简单的例子,它只计算整数列表的总和。这是模型,它存储列表和总数。它具有添加新值或清除列表的方法。请注意这些方法如何在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控制器,因此两者都在更新相同的数据。您可以在文本字段中输入一些值,然后键入&#34; show&#34;在命令行中,您将看到值。输入&#34;清除&#34;在命令行中,值将从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;两者都是相互独立的(它们都只取决于模型)。

答案 1 :(得分:1)

我认为 borderline 过于宽泛。其中一部分是:您的要求不清楚。您打算使用命令行,如:

java -jar whatever.jar -command A
java -jar whatever.jar -command B
java -jar whatever.jar -command C

所以 - 你反复调用java,而且what.jar基本上是一个客户端,需要一些服务器&#34;这是真正的工作,还是你想象的

java -jar whatever.jar
> Type your command:
> A
... ran command A
> Type your command:

显然,这在这里有很大的不同。

但最后,它还告诉我们解决方案的位置:通过将这些客户端从实际执行中解除耦合。

含义:你应该做两件事

  • 定义某些服务器必须提供的功能又名服务
  • 然后您可以研究创建使用这些服务的不同客户端的方法

避免将所有这些不同方面都烘焙到一个 main()方法中!

答案 2 :(得分:0)

GUI上的所有内容都是基于事件的。这意味着当您按下按钮或以另一种方式与JavaFX窗口交互时调用方法,例如选择列表中的项目。

我建议保持内部逻辑和GUI逻辑分离。 单击按钮时,您将调用链接到该按钮的handleButton(ActionEvent actionEvent)方法。此方法应调用其他一个实际包含逻辑的类中的方法。

您可以通过命令行使用扫描仪获取用户输入:

public String getUserInput() {
    Scanner scan = new Scanner(System.in);
    String s = scan.next();
    return s
}

您现在可以检查此用户输入字符串,并使用switch(s)语句连接相应的方法。

我不确定您何时希望通过命令行获取此输入,但我建议您在舞台中添加一个开发人员按钮,在按下时调用getUserInput()