如何在JavaFX2中的任务之间重置进度指示器? [英] How to reset progress indicator between tasks in JavaFX2?

查看:51
本文介绍了如何在JavaFX2中的任务之间重置进度指示器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的主屏幕UI上有一个进度指示器,该指示器由各种选项卡和服务共享. 每个TabController都有其自己的Service实例. 在我的MainController类中,对于每个选项卡,我都将每个Service的progress属性绑定到ProgressIndicator.

I have one progress indicator on my main screen UI that is shared by various tabs and services. Each TabController has its own instance of Service. In my MainController class, for each tab I have bound each Service's progress property to the ProgressIndicator.

@FXML
Region veil;
@FXML
ProgressIndicator progressDial;

  progressDial.progressProperty().bind(tabController.commandService.progressProperty());
    veil.visibleProperty().bind(tabController.commandService.runningProperty());
    progressDial.visibleProperty().bind(tabController.commandService.runningProperty());
    tabController.commandService.messageProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> ov, String t, String newValue) {
            addCommentary(newValue);
        }
    });

但是,我看到在第一个服务使用它之后,用于执行后续服务或任务的进度盘没有出现. 我想知道我是否滥用了ProgressIndicator,因为每个服务可能同时运行. 我猜想进度在第一次完成后不会重置.如何重设? 进度属性是只读的.

However I see that after the first service uses it, the progress dial does not appear for the execution of subsequent services or tasks. I am wondering if I am misusing the ProgressIndicator since each Service probably runs concurrently. I am guessing that the progress wasn't reset after the first finished. How do I reset it? The progress property is read only.

ReadOnlyDoubleProperty progressProperty()获取 ReadOnlyDoubleProperty表示进度.

ReadOnlyDoubleProperty progressProperty() Gets the ReadOnlyDoubleProperty representing the progress.

调用updateProgress(0)并没有使拨号重新出现.

And calling updateProgress(0) does nothing to make the dial reappear.

我试图使用ProgressIndicator作为全局显式重置它

I tried to explicitly reset it using the ProgressIndicator as a global

mainController.progressDial.setProgress(0);

但这失败了

java.lang.RuntimeException: A bound value cannot be set. at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:159)

java.lang.RuntimeException: A bound value cannot be set. at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:159)

我可能会误会,但是我认为这是JavaFX UI控件设计中的错误.将进度更新为0应该重置进度指示器.

I could be mistaken, but I think this is a fault in the JavaFX UI controls design. Updating progress to 0 should reset the progress Indicator.

推荐答案

我的回答中有些写作,因为从您的问题中我不清楚我的实例到底出了什么问题.希望答案中的解释或示例代码都有用.

There is a bit of writing in my answer because it's not exactly clear to me from your question what is going wrong with your instance. Hopefully either the explanation or the sample code in the answer is useful.

我可能会误会,但是我认为这是JavaFX UI控件设计中的错误.将进度更新为0应该重置进度指示器.

I could be mistaken, but I think this is a fault in the JavaFX UI controls design. Updating progress to 0 should reset the progress Indicator.

您有点误会.您已将指标的进度绑定到任务的进度.任务已完成且进度为1.现在,如果您要对另一个任务重复使用相同的指示器或使其测量其他任务的进度,则必须首先停止它以测量原始任务的进度.要取消关联原始任务的进度指示器,请

You are slightly mistaken. You have bound the progress of the indicator to the progress of a task. The task is completed and progress is 1. Now if you want to re-use the same indicator for another task or make it measure the progress of something else, you have to first stop it from measuring the progress of the original task. To disassociate the progress indicator for the original task, unbind it's progress. Once the progress indicator's progress is no longer bound to the original task's progress, you are free to set the indicator to whatever value you want, or bind it to something else.

类似地,您一次只能将进度指示器的进度绑定到一件事情(除非您双向绑定该指标,而不能与任务进度绑定,因为任务进度是只读的,并且双向绑定到无论如何,多个任务进度值将是错误的,因为每个任务将位于不同的进度点.

Similarly, you can only bind the progress indicator's progress to one thing at a time (unless you bi-directionally bind the indicator, which you can't do with task progress because task progress is read only and bi-directionally binding to multiple task progress values would be incorrect anyway as each task would be at a different progress point).

重新显示拨号盘.

make the dial reappear.

根据您的描述,我不确定拨盘为什么会首先消失,因此需要重新出现.通常,当进度指示器的进度达到1时,它仍保持可见状态,报告已完成的进度,它不会自动消失.您可能会将指示器的可见性设置为false或将其不透明度更改为零.这两个属性均与指标测量的实际进度无关.或者,也许您正在从显示的场景中删除指示器.如果要在完成任务后修改可见性并将指示器设置为不可见,并且希望随后再次看到它以测量另一个任务的进度,则需要确保它在场景中,并且不透明度> 0且可见性设置为true.

I'm not sure from your description why the dial would disappear in the first place so that it would need to reappear. Normally, when a progress indicator's progress reaches 1, it still stays visible reporting fully completed progress, it doesn't automatically disappear. You are likely setting the visibility of the indicator to false or modifying it's opacity to zero. Both of those properties have nothing to do with the actual progress measured by the indicator. Or maybe you are removing the indicator from the displayed scene. If you are modifying visibility and setting the indicator to invisible after a task is completed and you want to subsequently see it again to measure the progress of another task, then you will need to make sure it is in the scene, with opacity > 0 and visibility set to true.

建议

您只能运行一次任务,因此在完成任务后,如果已经完成了一些进度,将进度重新设置为零就没有多大意义.

You can only run a task once, so after it is done, it doesn't make a lot of sense to set it's progress back to zero if it had already made some progress.

属性类型

A 进度指示器的进度属性是普通的DoubleProperty,而不是ReadOnlyDoubleProperty,因此可以直接设置(只要它没有绑定到另一个值).

A progress indicator's progress property is a plain DoubleProperty, not a ReadOnlyDoubleProperty, so it is directly settable (as long as it is not bound to another value).

任务的进度属性是只读的,必须通过 updateProgress .任务的progress属性很可能是只读的,因此可以通过updateProgress例程中的特殊代码来确保对它的更新是线程安全的.

A task's progress property which is read only and must be changed via updateProgress. The task's progress property was likely made read only so that updates to it can be ensured to be threadsafe by special code in the updateProgress routine.

示例代码

考虑以下代码(我相信)可以实现您要尝试执行的操作.该代码模拟了铁人三项的运行,其中铁人三项的每个阶段(游泳,自行车,跑步)都是一个单独的任务.铁人三项赛进行时,进度指示器会显示铁人三项赛各阶段的进度.铁人三项赛结束后,进度指示器将消失,直到开始新的铁人三项赛.抱歉,样本太长了,我发现很难提出更简洁的内容.

Consider the following code which (I believe) accomplishes the intent of what you are trying to do. The code simulates running a triathlon where each stage (swim, bike, run) of the triathlon is a separate task. While a triathlon is being run, a progress indicator shows the progress of each stage of the triathlon. When the triathlon completes the progress indicator fades away until a new triathlon is started. Sorry the sample is so lengthy, I found it hard to come up with something more concise.

import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.beans.*;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.concurrent.Task;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Triathlon extends Application {

  private final Random random = new Random();
  private final ExecutorService exec = Executors.newSingleThreadExecutor();

  @Override public void start(Stage stage) throws Exception {
    final TaskMonitor taskMonitor = new TaskMonitor();

    final ProgressIndicator progressIndicator = new ProgressIndicator();
    progressIndicator.progressProperty().bind(
        taskMonitor.currentTaskProgressProperty()
    );

    final Label currentRaceStage = new Label();
    currentRaceStage.textProperty().bind(
        taskMonitor.currentTaskNameProperty()
    );

    createMainLayout(
        stage,
        createStartRaceButton(
            exec,
            taskMonitor
        ),
        createRaceProgressView(
            taskMonitor,
            progressIndicator,
            currentRaceStage
        )
    );
  }

  @Override public void stop() throws Exception {
    exec.shutdownNow();
  }

  private Button createStartRaceButton(final ExecutorService exec, final TaskMonitor taskMonitor) {
    final Button startButton = new Button("Start Race");
    startButton.disableProperty().bind(taskMonitor.idleProperty().not());
    startButton.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(ActionEvent actionEvent) {
        runRace(exec, taskMonitor);
      }
    });
    return startButton;
  }

  private HBox createRaceProgressView(final TaskMonitor taskMonitor, ProgressIndicator progressIndicator, Label currentRaceStage) {
    final HBox raceProgress = new HBox(10);
    raceProgress.getChildren().setAll(
      currentRaceStage,
      progressIndicator
    );
    raceProgress.setOpacity(0);
    raceProgress.setAlignment(Pos.CENTER);

    final FadeTransition fade = new FadeTransition(Duration.seconds(0.75), raceProgress);
    fade.setToValue(0);

    taskMonitor.idleProperty().addListener(new InvalidationListener() {
      @Override
      public void invalidated(Observable observable) {
        if (taskMonitor.idleProperty().get()) {
          fade.playFromStart();
        } else {
          fade.stop();
          raceProgress.setOpacity(1);
        }
      }
    });

    return raceProgress;
  }

  private void createMainLayout(Stage stage, Button startButton, HBox raceProgress) {
    final VBox layout = new VBox(10);
    layout.getChildren().setAll(
      raceProgress,
      startButton
    );
    layout.setAlignment(Pos.CENTER);
    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;");
    stage.setScene(new Scene(layout, 200, 130));
    stage.show();
  }


  private void runRace(ExecutorService exec, TaskMonitor taskMonitor) {
    StageTask swimTask = new StageTask("Swim", 30,   40);
    StageTask bikeTask = new StageTask("Bike", 210, 230);
    StageTask runTask  = new StageTask("Run",  120, 140);

    taskMonitor.monitor(swimTask, bikeTask, runTask);

    exec.execute(swimTask);
    exec.execute(bikeTask);
    exec.execute(runTask);
  }

  class TaskMonitor {
    final private ReadOnlyObjectWrapper<StageTask> currentTask = new ReadOnlyObjectWrapper<>();
    final private ReadOnlyStringWrapper currentTaskName        = new ReadOnlyStringWrapper();
    final private ReadOnlyDoubleWrapper currentTaskProgress    = new ReadOnlyDoubleWrapper();
    final private ReadOnlyBooleanWrapper idle                  = new ReadOnlyBooleanWrapper(true);

    public void monitor(final StageTask task) {
      task.stateProperty().addListener(new ChangeListener<Task.State>() {
        @Override
        public void changed(ObservableValue<? extends Task.State> observableValue, Task.State oldState, Task.State state) {
          switch (state) {
            case RUNNING:
              currentTask.set(task);
              currentTaskProgress.unbind();
              currentTaskProgress.set(task.progressProperty().get());
              currentTaskProgress.bind(task.progressProperty());
              currentTaskName.set(task.nameProperty().get());
              idle.set(false);
              break;

            case SUCCEEDED:
            case CANCELLED:
            case FAILED:
              task.stateProperty().removeListener(this);
              idle.set(true);
              break;
          }
        }
      });
    }

    public void monitor(final StageTask... tasks) {
      for (StageTask task: tasks) {
        monitor(task);
      }
    }

    public ReadOnlyObjectProperty<StageTask> currentTaskProperty() {
      return currentTask.getReadOnlyProperty();
    }

    public ReadOnlyStringProperty currentTaskNameProperty() {
      return currentTaskName.getReadOnlyProperty();
    }

    public ReadOnlyDoubleProperty currentTaskProgressProperty() {
      return currentTaskProgress.getReadOnlyProperty();
    }

    public ReadOnlyBooleanProperty idleProperty() {
      return idle.getReadOnlyProperty();
    }
  }

  class StageTask extends Task<Duration> {
    final private ReadOnlyStringWrapper name;
    final private int minMinutesElapsed;
    final private int maxMinutesElapsed;

    public StageTask(String name, int minMinutesElapsed, int maxMinutesElapsed) {
      this.name = new ReadOnlyStringWrapper(name);
      this.minMinutesElapsed = minMinutesElapsed;
      this.maxMinutesElapsed = maxMinutesElapsed;
    }

    @Override protected Duration call() throws Exception {
      Duration duration = timeInRange(
        minMinutesElapsed, maxMinutesElapsed
      );

      for (int i = 0; i < 25; i++) {
        updateProgress(i, 25);
        Thread.sleep((int) (duration.toMinutes()));
      }
      updateProgress(25, 25);

      return duration;
    }

    private Duration timeInRange(int min, int max) {
      return Duration.minutes(
        random.nextDouble() * (max - min) + min
      );
    }

    public ReadOnlyStringProperty nameProperty() {
      return name.getReadOnlyProperty();
    }
  }

  public static void main(String[] args) {
    Application.launch(Triathlon.class);
  }
}


更新其他问题

假设不是每个阶段的铁人三项,而是每个阶段都是一个独立的事件(例如在奥运会中).因此,游泳,骑自行车,跑步等都是SportService的实例.它们同时执行.体育场电子记分板上有一个进度指示器表盘,所有SportServices游泳,骑自行车,跑步等设备都可以共享它.它为我提供了大致的总体进度-尽管我意识到这很模糊,但是它是在不了解细节的情况下对所有进度的总结.每个事件.

Instead of being a triathlon, suppose each stage was instead, an independent event (like in the Olympics). So swim, bike, run etc. are instances of SportService. They execute concurrently. On the stadium electronic scoreboard is a progress indicator dial that is shared by all SportServices swim, bike, run etc. It gives me the approximate general progress - though I realize that is vague but is a summary of how everything is progressing without seeing the details of each event.

使用创建多个并行任务中定义的机制并行运行事件.一个>.为您的整体奥运会进度创建一个进度指示器,并使用低级绑定API将其绑定到所有任务的进度总和的进度.

Run the events in parallel using the mechanism defined in Creating multiple parallel tasks. Create a single progress indicator for your overall olympics progress and bind it to the progress of the sum of progress for all tasks using the low level binding api.

ObservableList<Service> services = FXCollections.observableArrayList();

. . .  add services to list.

// extract the progress property for each of the added services.
final ReadOnlyDoubleProperty[] taskProgressList = new ReadOnlyDoubleProperty[services.size()];
for (int i = 0; i < taskProgressList.length; i++) {
  taskProgressList[i] = services.get(i).progressProperty();
}

// calculate the average progress of all services.
DoubleBinding overallProgress =  Bindings.createDoubleBinding(new Callable<Double>() {
  @Override public Double call() throws Exception {
    double value = 0;

    for (int i = 0; i < taskProgressList.length; i++) {
      value += taskProgressList[i].get();
    }

    value /= taskProgressList.length;

    return value;
  }
}, taskProgressList);

// bind the overall progress to our indicator
ProgressIndicator overallProgressIndicator = new ProgressIndicator();
overallProgressIndicator.progressProperty().bind(overallProgress);

这是另一个示例,演示了对totalProgress DoubleBinding的使用.

Here is a another sample which demonstrates use of the overallProgress DoubleBinding.

import java.io.*;
import java.net.URL;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class FirstLineSequentialVsParallelService extends Application {
  private static final String[] URLs = {
    "http://www.google.com", 
    "http://www.yahoo.com", 
    "http://www.microsoft.com", 
    "http://www.oracle.com" 
  };

  private ExecutorService sequentialFirstLineExecutor;
  private ExecutorService parallelFirstLineExecutor;

  @Override public void init() throws Exception {
    sequentialFirstLineExecutor = Executors.newFixedThreadPool(
      1, 
      new FirstLineThreadFactory("sequential")
    );  

    parallelFirstLineExecutor = Executors.newFixedThreadPool(
      URLs.length, 
      new FirstLineThreadFactory("parallel")
    );  
  }

  @Override
  public void stop() throws Exception {
    parallelFirstLineExecutor.shutdown();
    parallelFirstLineExecutor.awaitTermination(3, TimeUnit.SECONDS);

    sequentialFirstLineExecutor.shutdown();
    sequentialFirstLineExecutor.awaitTermination(3, TimeUnit.SECONDS);
  }

  public static void main(String[] args) { launch(args); }
  @Override public void start(Stage stage) throws Exception {
    final VBox messages = new VBox();
    messages.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");

    messages.getChildren().addAll(
      new Label("Parallel Execution"), 
      new Label("------------------")
    );
    DoubleBinding parallelProgress = fetchFirstLines(messages, parallelFirstLineExecutor);
    ProgressMonitoredLabel parallelProgressSummary = new ProgressMonitoredLabel("Parallel Execution Summary");
    parallelProgressSummary.progress.progressProperty().bind(parallelProgress);
    messages.getChildren().add(parallelProgressSummary);

    messages.getChildren().addAll(
      new Label("Sequential Execution"), 
      new Label("--------------------")
    );
    DoubleBinding  sequentialProgress = fetchFirstLines(messages, sequentialFirstLineExecutor);
    ProgressMonitoredLabel sequentialProgressSummary = new ProgressMonitoredLabel("Sequential Execution Summary");
    sequentialProgressSummary.progress.progressProperty().bind(sequentialProgress);
    messages.getChildren().add(sequentialProgressSummary);

    messages.setStyle("-fx-font-family: monospace;");

    stage.setScene(new Scene(messages, 600, 650));
    stage.show();
  }

  private DoubleBinding fetchFirstLines(final VBox monitoredLabels, ExecutorService executorService) {
    ObservableList<Service> services = FXCollections.observableArrayList();
    for (final String url: URLs) {
      final FirstLineService service = new FirstLineService();
      service.setExecutor(executorService);
      service.setUrl(url);

      final ProgressMonitoredLabel monitoredLabel = new ProgressMonitoredLabel(url);
      monitoredLabels.getChildren().add(monitoredLabel);
      monitoredLabel.progress.progressProperty().bind(service.progressProperty());

      service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
        @Override public void handle(WorkerStateEvent t) {
          monitoredLabel.addStrings(
            service.getMessage(),
            service.getValue()
          );
        }
      });
      service.start();

      services.add(service);
    }

    final ReadOnlyDoubleProperty[] taskProgressList = new ReadOnlyDoubleProperty[services.size()];
    for (int i = 0; i < taskProgressList.length; i++) {
      taskProgressList[i] = services.get(i).progressProperty();
    }

    return Bindings.createDoubleBinding(new Callable<Double>() {
      @Override public Double call() throws Exception {
        double value = 0;

        for (int i = 0; i < taskProgressList.length; i++) {
          value += taskProgressList[i].get();
        }

        value /= taskProgressList.length;

        return value;
      }
    }, taskProgressList);
  }

  public class ProgressMonitoredLabel extends HBox {
    final ProgressBar progress;
    final VBox labels;

    public ProgressMonitoredLabel(String initialString) {
      super(20);

      progress = new ProgressBar();
      labels   = new VBox();
      labels.getChildren().addAll(new Label(initialString), new Label());

      progress.setPrefWidth(100);
      progress.setMinWidth(ProgressBar.USE_PREF_SIZE);
      HBox.setHgrow(labels, Priority.ALWAYS);
      setMinHeight(60);

      getChildren().addAll(progress, labels);
    }

    public void addStrings(String... strings) {
      for (String string: strings) {
        labels.getChildren().add(
          labels.getChildren().size() - 1,
          new Label(string)
        );
      }
    }
  }

  public static class FirstLineService extends Service<String> {
    private StringProperty url = new SimpleStringProperty(this, "url");
    public final void setUrl(String value) { url.set(value); }
    public final String getUrl() { return url.get(); }
    public final StringProperty urlProperty() { return url; }
    protected Task createTask() {
      final String _url = getUrl();
      return new Task<String>() {
        { updateProgress(0, 100); }
        protected String call() throws Exception {
          updateMessage("Called on thread: " + Thread.currentThread().getName());
          URL u = new URL(_url);
          BufferedReader in = new BufferedReader(
                  new InputStreamReader(u.openStream()));
          String result = in.readLine();
          in.close();

          // pause just so that it really takes some time to run the task 
          // so that parallel execution behaviour can be observed.
          for (int i = 0; i < 100; i++) {
            updateProgress(i, 100);
            Thread.sleep(50); 
          }

          return result;
        }
     };
    }
  }

  static class FirstLineThreadFactory implements ThreadFactory {
    static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final String type;

    public FirstLineThreadFactory(String type) {
      this.type = type;
    }

    @Override public Thread newThread(Runnable runnable) {
      Thread thread = new Thread(runnable, "LineService-" + poolNumber.getAndIncrement() + "-thread-" + type);
      thread.setDaemon(true);

      return thread;
    }
  }  
}

这篇关于如何在JavaFX2中的任务之间重置进度指示器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆