我有一个可以在控制台和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方法不会被触发。我弄错了什么?即使是最终完成的印刷品也不会被写入。
答案 0 :(得分:0)
如评论中所述,JavaFX Runnable
中属性的更新是在FX应用程序线程上执行的。该线程是用于呈现JavaFX场景图的线程,并在启动时由JavaFX工具包启动。如果JavaFX工具包没有运行,那么线程将无法启动,并且不会更新这些属性。
如果您想要一些可以在需要在JavaFX应用程序之外运行的后台运行的东西,那么您应该使用不依赖于JavaFX的线程工具。在这种情况下,您只需实施Callable
或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();
然后使用
在后台线程中执行此操作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)
像往常一样。