multithreading - 在所有工作完成之前,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);
}
}
}
解决方案
我跟进了 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();
这行得通,我很满意,但我不能做的是改变进度条的颜色。
推荐阅读
- parameters - 如何定义具有不同数量参数的函数?
- r - 多个点之间的最近点
- excel - 插入一系列单元格而不是插入整行
- php - 在服务器上找不到类 Google_client,但在 localhost 上没问题
- apache-spark - 将 Spark 的本地 tmp 文件夹放在共享目录中是否安全
- android - Google Play:未显示用于删除先前声明的新 APK 权限的表单字段“合规状态”
- python - TypeError:需要一个类似字节的对象,而不是已知纯文本攻击中的“str”
- android - 广播接收器在外部存储上获取媒体更改的正确意图过滤器是什么?
- linux - Linux tr 命令失败“未对齐的构造”
- sql - 如果存在NULL,则排除多个条目的内部联接查询