首页 > 解决方案 > 在所有工作完成之前,Java FXML ProgressBar 不会更新

问题描述

我正在尝试通过移植命令行 Java 应用程序来了解使用 Scene Builder 和 NetBeans 的 Java FX,并且大部分时间都可以正常工作。但是,在工作人员完成之前,进度条和文本区域都不会更新。“处理图像”按钮并不表示已单击,5 秒或更长时间后,光标显示应用程序正忙。我使用 NetBeans->New Project->JavaFX->Java FXML Application 启动了这个项目。我的代码在控制器文件中。

我了解到(感谢 Google)这是这种 GUI 的正常行为,更新需要在它们自己的线程中,而不是如何创建我需要的线程。

我创建了一个嵌套线程只是为了增加进度条,它没有用。然后我为使用进度线程的工作人员创建了一个嵌套线程,没有任何改变。然后我将进度线程嵌套在嵌套的工作者中,但仍然没有变化。工作方法/线程管理过滤它们的文件列表,并一次将正确的文件提交给纠正可能发现的任何错误的方法。工作线程负责更新进度条,文本区域的更新由“修复”文件的方法负责。

单击此按钮开始处理。

    @FXML
private void handleBtnProcessImagesClick(ActionEvent event) {
    btnProcess.setText("Processing");
    if (!sourceDirExists) {
        updateLog("Need valid source folder to process images.",
                WARNING);
        return;
    }
    sourceDir = new File(txtFldSourceFolder.getText());
    createDestFolder();
    //processFiles();
    ThreadProcessImages pf = new ThreadProcessImages();
    pf.run();
}

主要工人

class ThreadProcessImages extends Thread {

    /**
 * manage the progress bar while calculations are running
 */
class ThreadProgress extends Thread {

    double progress = 0.0;
    double max = 1.0 - 0.0005;

    ThreadProgress(double progress) {
        this.progress = progress;
    }

    @Override
    public void run() {
        if (progress == 0.0) {
            progressBar.setStyle("-fx-accent: SKYBLUE;");
            progressBar.setProgress(0.0);
            System.out.println("Thread start.");
        } else if (progress >= max) {
            progressBar.setStyle("-fx-accent: SPRINGGREEN;");
            progressBar.setProgress(1.0);
            System.out.println("Thread end.");
        } else {
            progressBar.setProgress(progress);

        }
    }

}
@Override
    public void run() {
        if (chkBoxSaveEdits.isSelected() && isEdited) {
            try {
                appProps.saveProperties();
                isEdited = false;
            } catch (IOException ex) {
                updateLog("processFiles can't save edits.", SEVERE);
            }

        }
        if (chkBxVerbose.isSelected()) {
            updateLog("FileName" + Common.COMMA + "HighCut" + Common.COMMA
                    + "LowCut" + Common.COMMA + "MaxDN" + Common.COMMA + "MinDN"
                    + Common.COMMA + "NewMax" + Common.COMMA + "newMin"
                    + Common.COMMA + "numHighs" + Common.COMMA + "numLows");
        }

        long start = System.nanoTime();
        // get a sorted list of files in the folder
        File[] listOfFiles = sourceDir.listFiles();
        Arrays.sort(listOfFiles, (f1, f2) -> f1.compareTo(f2));

        // ignores directoriesI
        int numFilesFound = 0;
        int numTIFsFound = 0;
        double progBarMax = (double) listOfFiles.length;
        ThreadProgress progThread = new ThreadProgress(0.0);
        progThread.run();
        try {
            Thread.sleep(1l, 0);
        } catch (InterruptedException ex) {
            Logger.getLogger(FixBsPixController.class.getName()).log(Level.SEVERE, null, ex);
        }
        for (File inFile : listOfFiles) { // Filter files
            if (inFile.isFile()) {  // exclude folders
                ++numFilesFound;
                // Process only the files of interest
                if (inFile.getName().contains(METADATA)) {
                    copyFile(inFile, new File(destinationDir.getAbsolutePath()
                            + Common.PATH_SEP + inFile.getName()));
                } else {
                    if (inFile.getName().contains(ZIPTIF)
                            || inFile.getName().contains(TIF)) {
                        if ((inFile.getName().contains(BWB)
                                && chkBoxBWB.isSelected())
                                || (inFile.getName().contains(B08)
                                && chkBoxB08.isSelected())
                                || (inFile.getName().contains(B10)
                                && chkBoxB10.isSelected())
                                || (inFile.getName().contains(B12)
                                && chkBoxB12.isSelected())) {
                            ++numTIFsFound;
                            File outFile = new File(destinationDir
                                    + Common.PATH_SEP + inFile.getName());
                            // The file is corrected here
                            fixThePix(inFile, outFile);
                        }
                    }
                    progThread
                            = new ThreadProgress(numTIFsFound / progBarMax);
                    progThread.run();
                    try {
                        Thread.sleep(1l, 0);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(FixBsPixController.class.getName()).log(Level.SEVERE, null, ex);
                    }

                }
            }
        } 
        progThread
                = new ThreadProgress(1.0);
        progThread.run();
        try {
            Thread.sleep(1l, 0);
        } catch (InterruptedException ex) {
            Logger.getLogger(FixBsPixController.class.getName()).log(Level.SEVERE, null, ex);
        }
        btnProcess.setText("Process Images");

        if (numTIFsFound == 0) {
            updateLog("No image files found! Wrong folder?", WARNING);
            updateLog("Might want to delete " + destinationDir, INFO);
        } else {
            updateLog("Files processed " + numFilesFound
                    + "  TIFFs found: " + numTIFsFound, INFO);
            long elapsedTime = System.nanoTime() - start;
            updateLog("Elapsed time: " + elapsedTime / 1_000_000 + " ms.", INFO);
            updateLog(" That's "
                    + ((double) elapsedTime / (double) numFilesFound) / 1_000_000
                    + " ms per file.", INFO);
        }

    }

}

标签: multithreadingjavafxprogress-bardesktop-applicationscenebuilder

解决方案


我跟进了 James_D 的评论并编写了一个可运行的线程来操作 ProgressBar 的进度。

/** ThreadProgress updates the progress-bar in real time if
  * started with '.start()'
  */
class ThreadProgress implements Runnable {

    ProgressBar progressBar;

    ThreadProgress(double progress, ProgressBar progressBar) {
        this.progress = progress;
        this.progressBar = progressBar;
    }

    @Override
    public void run() {
        progressBar.setProgress(progress);
    }
}

我还将工作放入它自己的可运行线程中,并在“handleBtnProcessImagesClick”方法中启动它。

@FXML
private void handleBtnProcessImagesClick(ActionEvent event) {
    Thread qif = new Thread(new QueueImageFiles());
    qif.start();
}

然后,在 qif (worker) 线程中,我在需要时更新了 ProgressBar。

// first invocation
Thread progThread = new Thread(new ThreadProgress(0.0, progressBar));
progThread.start();

这行得通,我很满意,但我不能做的是改变进度条的颜色。


推荐阅读