从线程更新进度条和多个标签 [英] Update progress bar and multiple labels from thread

查看:150
本文介绍了从线程更新进度条和多个标签的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个JavaFX应用程序,让用户选择一个文件夹,然后解析它的内容以查找MP3文件并读取它们的元数据。



我得到了这个使用Swing虽然我发现很难让用户界面看起来很好。因此,我正在尝试在JavaFX中执行相同的操作。



在原始的Swing应用程序中,我创建了一个线程,用于启动用户选择的文件夹中的文件解析。它的工作方式如下:


  1. 找出要解析的文件总数 - 文件数和文件夹数连续呈现为两个UI中的单独标签

  2. 解析所有文件以查找哪些文件是MP3文件并存储元数据 - 找到的MP3文件数量连续显示在UI中的标签中

同时更新标签,显示发生的事件的状态,以及反映上述两个步骤进度的进度条。第一步的进度占总进度的30%,而第二步的进度占另外70%。



我找到了如何绑定任务的示例到进度条,但我还需要更新四个标签:状态,文件数,文件夹数和MP3数。



我相信我可以处理其中一个标签使用 updateMessage ,虽然我不知道如何管理其他三个。

解决方案

使用多个任务将问题拆分为多个位。使用控制任务来监视子任务的状态和总体进度。使用以获取更多信息,并链接到JavaFX中有关并发性的更多资源。


I'm working on a JavaFX application that let's the user select a folder and then parse it's contents to find MP3 files and read their metadata.

I got this working with Swing although I found it hard to make the user interface look good. Thus I'm trying to do the same in JavaFX.

In the original Swing app, I create a thread which starts the parsing of files in the folder the user selected. It works like this:

  1. Find out total number of files to parse - The number of files and the number of folders are continuously presented in two separate labels in the UI
  2. Parse all files to find which ones are MP3 files and store the metadata - The number of found MP3 files are continuously presented in a label in the UI

At the same time a label is updated that shows the status of whats happening and also a progress bar reflecting the progress of the two above steps. The progress of the first step makes up 30 % of the total progress while the second step makes up the other 70 %.

I found an example of how to bind the task to the progress bar, but I also need to update the four labels: status, file count, folder count and MP3 count.

I believe I can handle one of the labels with updateMessage, although I don't know how to manage the other three.

解决方案

Use multiple Tasks to split the problem into bits. Use a controlling tasks to monitor status and overall progress of subtasks. Use java.util.concurrent classes to manage Task execution, sequencing and data structures such as LinkedBlockingDeque.

This recommended solution is not the simplest solution to your problem, but should offer a good user experience if done well.


For an example of a divide and conquer approach applied to different problems, see the following code samples:

  1. splits a complex process into multiple managed subtasks.
  2. demonstrates management of execution of multiple workers sequentially or in parallel.


A potential simple alternate approach is to use a single Task for the whole process and report your multiple feedback values back to your JavaFX UI by invoking Platform.runLater from your Task code as needed.

See the Task documentation section "A Task Which Modifies The Scene Graph" for an example of this approach.

Here is something which updates multiple labels at once inside a Platform.runLater call.

Platform.runLater(new Runnable() {
  @Override public void run() {
    status.setText("");
    folderCount.setText("");
    fileCount.setText("");
    mp3Count.setText("");
  }
});

And some code which is similar to your example:

import java.util.Arrays;
import java.util.List;
import static javafx.application.Application.launch;
import javafx.application.*;
import javafx.beans.value.*;
import javafx.concurrent.Task;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class Mp3Finder extends Application {
  final Label status = new Label();
  final Label folderCount = new Label();
  final Label fileCount = new Label();
  final Label mp3Count = new Label();

  @Override public void start(Stage stage) {
    final GridPane finderResults = new GridPane();
    finderResults.setPrefWidth(400);
    finderResults.setVgap(10);
    finderResults.setHgap(10);
    finderResults.addRow(0, new Label("Status: "),    status);
    finderResults.addRow(1, new Label("# Folders: "), folderCount);
    finderResults.addRow(2, new Label("# Files: "),   fileCount);
    finderResults.addRow(3, new Label("# mp3s: "),    mp3Count);

    final Button finderStarter = new Button("Find mp3s");
    finderStarter.setOnAction(new EventHandler<ActionEvent>() {
      @Override public void handle(ActionEvent t) {
        startMp3Finder(finderStarter);
      }
    });

    VBox layout = new VBox(10);
    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10; -fx-font-size: 16;");
    layout.getChildren().setAll(finderStarter, finderResults);
    stage.setScene(new Scene(layout));
    stage.show();
  }

  private void startMp3Finder(final Node starterNode) {
    starterNode.setDisable(true);

    Mp3FinderTask task = new Mp3FinderTask(status, folderCount, mp3Count);
    task.runningProperty().addListener(new ChangeListener<Boolean>() {
      @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean wasRunning, Boolean isRunning) {
        if (!isRunning) {
          starterNode.setDisable(false);
        }
      }
    });

    final Thread thread = new Thread(task , "mp3-finder");
    thread.setDaemon(true);
    thread.start();
  }

  private class Mp3FinderTask extends Task<List<String>> {
    private final Label status;
    private final Label folderCount;
    private final Label mp3Count;

    public Mp3FinderTask(Label status, Label folderCount, Label mp3Count) {
      this.status = status;
      this.folderCount = folderCount;
      this.mp3Count = mp3Count;
    }

    @Override protected List<String> call() throws Exception {
      initFinderResults();

      updateLabelLater(status, "Finding Folders");
      setProgressIndicator(folderCount);
      List folders = findFolders();
      updateLabelLater(folderCount, folders.size() + "");

      updateLabelLater(status, "Finding Files");
      setProgressIndicator(fileCount);
      List files = findFiles(folders);
      updateLabelLater(fileCount, files.size() + "");

      updateLabelLater(status, "Find mp3s");
      setProgressIndicator(mp3Count);
      List mp3s = findMp3s(files);
      updateLabelLater(mp3Count, mp3s.size() + "");

      updateLabelLater(status, "All mp3s Found");

      return mp3s;
    }

    void updateLabelLater(final Label label, final String text) {
      Platform.runLater(new Runnable() {
        @Override public void run() {
          label.setGraphic(null);
          label.setText(text);
        }
      });
    }

    private List<String> findFolders() throws InterruptedException { 
      // dummy implementation
      Thread.currentThread().sleep(1000);
      return Arrays.asList("folder1", "folder2", "folder3");
    }

    private List<String> findFiles(List<String> folders) throws InterruptedException {
      // dummy implementation
      Thread.currentThread().sleep(1000);
      return Arrays.asList("file1", "file2", "file3", "file4", "file5");
    }

    private List<String> findMp3s(List<String> files) throws InterruptedException {
      // dummy implementation
      Thread.currentThread().sleep(1000);
      return Arrays.asList("music1", "music2");
    }

    private void initFinderResults() {
      Platform.runLater(new Runnable() {
        @Override public void run() {
          status.setText("");
          folderCount.setText("");
          fileCount.setText("");
          mp3Count.setText("");
        }
      });
    }

    private void setProgressIndicator(final Label label) {
      Platform.runLater(new Runnable() {
        @Override public void run() {
          label.setGraphic(new ProgressIndicator());
        }
      });
    }
  }

  public static void main(String[] args) { launch(args); }
}


See the StackOverflow question on use of Platform.runLater and accessing the UI from a different thread for more information for more information and links to further resources on concurrency in JavaFX.

这篇关于从线程更新进度条和多个标签的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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