Changelistener未在控制台中触发,而是由GUI触发

时间:2017-04-06 12:48:58

标签: java task command-line-interface listener

我有一个可以在控制台和GUI上运行和更新的任务。考虑我的任务写为

        public static Task<Void> doStuff() {
    Task<Void> task;
    task = new Task<Void>() {
        final int totalSteps = 4;
        @Override
        public Void call() throws Exception {

            updateProgress(0, totalSteps);
            updateMessage("1");
            action(1);
            updateProgress(0, totalSteps);
            updateMessage("2");
            action(2);
            //etc..
            return null;
        }
    };
    new Thread(task)
            .start();
    return task;
}

通过在我的JavaFX GUI中绑定Progress和Message属性,一切都按预期工作,GUI根据Progress进行更新。 在我的CLI中,我尝试构建一个简单的进度条,用于更新用户有关操作进度的信息

private static void progressBar(Task task) {

    task.progressProperty().addListener((new ChangeListener() {
        @Override
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {
            // Return to line beginning
            System.out.print("\r");
            int percentage = (int) (100 * task.progressProperty().get());
            System.out.format("[%3d%%] %s", percentage, task.messageProperty().get());

            if (percentage == 100) {
                System.out.println("Finished");
            }
        }
    }));

}

}

据我所知,调试时,Change Listeners的Change方法不会被触发。我弄错了什么?即使是最终完成的印刷品也不会被写入。

1 个答案:

答案 0 :(得分:0)

如评论中所述,JavaFX Runnable中属性的更新是在FX应用程序线程上执行的。该线程是用于呈现JavaFX场景图的线程,并在启动时由JavaFX工具包启动。如果JavaFX工具包没有运行,那么线程将无法启动,并且不会更新这些属性。

如果您想要一些可以在需要在JavaFX应用程序之外运行的后台运行的东西,那么您应该使用不依赖于JavaFX的线程工具。在这种情况下,您只需实施Callablejava.util.function即可。如果要在状态更改时提供通知,请创建一个回调,以便在状态更改时调用。使用BiConsumer<String, Integer>包中的接口最好地表示回调。

在你展示的情况下,看起来你真的只需要消息和进展,所以你可以使用public class DoStuff implements Runnable { private final int totalSteps ; private BiConsumer<String, Integer> update ; public DoStuff(int totalSteps) { this.totalSteps = totalSteps ; } public void setUpdate(BiConsumer<String Integer> update) { this.update = update ; } @Override public void run() { if (update != null) { update.accept("0", 0) ; } for (int i = 1 ; i <= totalSteps ; i++) { action(i); if (update != null) { update.accept(Integer.toString(i), i); } } } private void action(int i) { // ... } public int getTotalSteps() { return totalSteps() ; } }

public void progressBar(DoStuff doStuff) {
    doStuff.setUpdate((s, p) -> {

        // Return to line beginning
        System.out.print("\r");
        int percentage = 100 * p / doStuff.getTotalSteps();
        System.out.format("[%3d%%] %s", percentage, s);

        if (percentage == 100) {
            System.out.println("Finished");
        }
    });
}

现在你会做

new Thread(doStuff).start();

然后使用

在后台线程中执行此操作
DoStuff doStuff = ... ;
doStuff.setUpdate((s, p) -> Platform.runLater(() -> {
    label.setText(s);
    double progress = 1.0 * p / doStuff.getTotalSteps();
    progressBar.setProgress(p);
}));

如果你想在JavaFX环境中使用它,你可以,但要确保你的回调更新FX应用程序线程上的任何UI组件:

Task<Void>

创建一个包含它的DoStuff doStuff = ... ; Task<Void> doStuffTask = new Task<Void>() { @Override protected Void call() { doStuff.setUpdate((s, p) -> { updateProgress(p, doStuff.getTotalSteps()); updateMessage(s); }); doStuff.run(); return null ; } }; 并以通常的JavaFX方式公开进度和消息也相当容易:

progressBar.progressProperty().bind(doStuffTask.progressProperty());
label.textProperty().bind(doStuffTask.messageProperty());

Thread t = new Thread(doStuffTask);
t.setDaemon(true);
t.start();

然后做

import time

start=time.time()    
for k in tablestrings:
    print k
    time.sleep(0.2)
像往常一样。