2017-04-06 61 views
0

我有一个任务,应该可以在控制台和GUI上运行和更新。考虑我的任务写成Changelistener没有在控制台中触发,但通过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内胶的进展和消息属性,一切正常,并根据进展情况GUI得到更新。 在我的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"); 
      } 
     } 
    })); 

} 

}

用户至于我可以调试看到,更改侦听器改变方法不会得到触发。我为此设置了错误?即使在最后的完成打印不会被写入。

+1

如何运行CLI示例? “Task”类被设计为与FX线程一起工作,所以我猜想如果JavaFX基础结构未初始化,它可能甚至不会发送进度更新和消息,因为将不存在FX UI线程... – Itai

+0

CLI已启动在Standart Java的主类中。我试图在一个线程中调用进度条,但这并不起作用。那么我需要将CLI作为JavaFX线程来调用吗?这甚至可以在不支持GUI的环境中使用吗? – chenino

+1

我不确定这是否可能,但即使它可能也不会令人满意。如果你需要一个没有JavaFX的解决方案,你可能应该考虑不使用'Task',因为'Task'似乎与JavaFX紧密相连。 – Itai

回答

0

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

如果您希望可以在需要在JavaFX应用程序外运行的背景中运行某些内容,那么您应该使用不依赖于JavaFX的线程工具。在这种情况下,您可以简单地实施RunnableCallable。如果您想在状态更改时提供通知,请创建一个回调,以在状态更改时调用。回调最好使用来自java.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(); 

执行此如果你想在一个JavaFX环境中使用此,你可以,但请确保您的回调更新FX应用程序线程上的任何UI组件:

DoStuff doStuff = ... ; 
doStuff.setUpdate((s, p) -> Platform.runLater(() -> { 
    label.setText(s); 
    double progress = 1.0 * p/doStuff.getTotalSteps(); 
    progressBar.setProgress(p); 
})); 

这也是相当容易地创建一个Task<Void>一个包装此,并公开在平时的JavaFX方式的进展和消息:

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

然后做

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

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

如常。

相关问题