JavaFX进度指示器未跨线程更新

时间:2018-07-11 17:19:07

标签: java multithreading javafx concurrency progress-bar

我正在尝试在JavaFX Interface类中实现一个进度指示器,该类将相应地更新为我的“ Reader”类,该类通过日志进行解析并跟踪其进度。

我目前的方法是创建一个单独的线程来运行Reader(这是因为接口在运行Reader时将变得无响应),并且随着Reader的进展,它将直接更新Interfaces进度指示器。

接口类:

public class Interface extends Application {

public ProgressBar pb = new ProgressBar(0);
public ProgressIndicator pi = new ProgressIndicator(0);

private BorderPane bp = new BorderPane();
private Stage stage = new Stage();
private Scene scene = new Scene(bp, 400, 190);
private Button run = new Button();
private Button browseInputPath = new Button();
private Button browseOutputPath = new Button();
private CheckBox runAutomatically = new CheckBox("Run Automatically");
private TextField inputPath = new TextField();
private TextField outputPath = new TextField();
private HBox InputPath = new HBox(5);
private HBox OutputPath = new HBox(5);
private HBox progress = new HBox(10);
private File configFile = new File("config.txt");
private JFileChooser fc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());

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

@Override
public void start(Stage arg0) throws Exception {

    initializeConfigFile();
    initializeHBoxes();
    initializeProps();

    bp.setTop(progress);
    bp.setBottom(OutputPath);
    bp.setCenter(InputPath);

    stage.setResizable(false);
    stage.setScene(scene);
    stage.show();

    if ( runAutomatically.isSelected() ) { runReader(); }

}

@SuppressWarnings("deprecation")
private void runReader() {

    run.setDisable(true);
    browseInputPath.setDisable(true);
    browseOutputPath.setDisable(true);
    inputPath.setDisable(true);
    outputPath.setDisable(true);

    Reader reader = new Reader(inputPath.getText(), outputPath.getText());
    Thread t = new Thread(reader, "thread");
    t.start();

    stage.setOnCloseRequest(event -> { t.stop(); });

}

private void writeConfigFile(Boolean runAuto, String inputPath, String outputPath) throws FileNotFoundException {

    PrintWriter printer = new PrintWriter(new FileOutputStream(configFile, false));
    printer.println("RunAutomatically: " + runAuto);
    printer.println("InputDirectory: " + inputPath);
    printer.println("OutputDirectory: " + outputPath);
    printer.close();

}

private void initializeConfigFile() throws IOException {

    if ( !configFile.exists() ) {

        File inputFolder = new File("input");
        File outputFolder = new File("output");
        inputFolder.mkdir();
        outputFolder.mkdir();
        writeConfigFile(false, inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath());

    } 

    Scanner input = new Scanner(configFile);

    while ( input.hasNext() ) {

        if ( input.next().contains("RunAutomatically:") ) {

            String temp = input.next();

            if ( temp.contains("true")) { 

                runAutomatically.setSelected(true);

            } else { runAutomatically.setSelected(false); }
        }

        if ( input.next().contains("InputDirectory:") ) { inputPath.setText(input.next()); }

        if ( input.next().contains("OutputDirectory:") ) { outputPath.setText(input.next()); }

    }

    input.close();
}

private void initializeHBoxes() {

    InputPath.setPadding(new Insets(15, 12, 10, 12 ));
    InputPath.setStyle("-fx-background-color: #336699;");
    InputPath.getChildren().add(inputPath);
    InputPath.getChildren().add(browseInputPath);

    OutputPath.setPadding(new Insets(15, 12, 15, 12 ));
    OutputPath.setStyle("-fx-background-color: #336699;");
    OutputPath.getChildren().add(outputPath);
    OutputPath.getChildren().add(browseOutputPath);

    progress.setPadding(new Insets(15, 12, 15, 12 ));
    progress.setStyle("-fx-background-color: #336699;");
    progress.getChildren().add(run);
    progress.getChildren().add(runAutomatically);

    progress.getChildren().add(pb);
    progress.getChildren().add(pi);

}

private void initializeProps() {

    run.setText("Start");
    run.setPrefSize(75, 40);
    Tooltip runHelper = new Tooltip("Begins process to parse log files. Files are taken from input directory path and completed files are placed in output directory path.");
    Tooltip.install(run, runHelper);
    run.setOnAction(event -> { runReader(); });

    pb.setPrefSize(125, 30);
    Tooltip pbHelper = new Tooltip("Total Transactions Completed for File");
    Tooltip.install(pb, pbHelper);

    pi.setPrefSize(100, 100);
    Tooltip piHelper = new Tooltip("Total Files Completed");
    Tooltip.install(pi, piHelper);

    browseInputPath.setText(" ... ");
    browseInputPath.setPrefSize(75, 30);
    browseInputPath.setOnAction(event -> {

        fc.setDialogTitle("Choose Input Directory");
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int returnValue = fc.showOpenDialog(null);

        if ( returnValue == JFileChooser.APPROVE_OPTION) { inputPath.setText(fc.getSelectedFile().getAbsolutePath()); }

    });

    browseOutputPath.setText(" ... ");
    browseOutputPath.setPrefSize(75, 30);
    browseOutputPath.setOnAction(event -> {

        fc.setDialogTitle("Choose Output Directory");
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int returnValue = fc.showOpenDialog(null);

        if ( returnValue == JFileChooser.APPROVE_OPTION) { outputPath.setText(fc.getSelectedFile().getAbsolutePath()); }

    });

    runAutomatically.setOnAction(e -> { 

        try {

            if ( runAutomatically.isSelected() ) {

                writeConfigFile(true, inputPath.getText(), outputPath.getText()); 

            } else { writeConfigFile(false, inputPath.getText(), outputPath.getText()); }

        } catch (FileNotFoundException e2 ) { e2.printStackTrace(); }

    });

    Tooltip autoRunHelper = new Tooltip("If selected, program will automatically run on program startup.");
    Tooltip.install(runAutomatically, autoRunHelper);

    inputPath.setPrefSize(300, 30);
    Tooltip inputPathHelper = new Tooltip("Input Path Directory");
    Tooltip.install(inputPath, inputPathHelper);

    outputPath.setPrefSize(300, 30);
    Tooltip outputPathHelper = new Tooltip("Output Path Directory");
    Tooltip.install(outputPath, outputPathHelper);  
    }
}

阅读器类:

public class Reader extends Interface implements Runnable {

private ArrayList<ArrayList<String>> database;
private String storeInfo;
private String date;
private String inputDirectory;
private String outputDirectory;
private int finalTransactionNumber;
private int firstTransactionNumber;
private int totalTransactions = 0;
private int completedTransactions = 0;
private int totalFiles = 0;
private int completedFiles = 0;

public Reader(String inputDirectory, String outputDirectory) {

    this.inputDirectory = inputDirectory;
    this.outputDirectory = outputDirectory;

}

public void run() {

    try {

        File[] directory = new File(inputDirectory).listFiles();
        totalFiles = directory.length;

        for ( int i = 0; i < totalFiles; i++ ) {

            completedTransactions = 0;
            initializeArrayList(directory[i]);
            storeInfo = retrieveStoreInfo(directory[i]);
            date = retrieveLogDate(directory[i]);

            new File(outputDirectory + "\\" + date).mkdirs();
            File output = new File(outputDirectory + "\\" + date + "\\" + storeInfo + ".txt");

            readFile(directory[i]);
            writeTransactionInfo(output);
            updateFileProgress();

        }

    } catch (FileNotFoundException e) { e.printStackTrace(); } 
}

/**
 * Updates progress bar from extended interface class by calculating completed transactions per file.
 * @throws InterruptedException 
 */
private void updateTransactionProgress() { 

    if ( totalTransactions == 0 ) { super.pb.setProgress(0); }

    completedTransactions++;
    super.pb.setProgress( (completedTransactions + 0.0) / totalTransactions );

    System.out.println(pb.getProgress());
}

/**
 * Updates progress indicator (pie chart) from extended interface class by calculating total completed files.
 */
private void updateFileProgress() {

    if ( totalFiles == 0 ) { super.pi.setProgress(0); }

    completedFiles++;
    super.pi.setProgress( (completedFiles + 0.0) / totalFiles );

}

/**
 * Scans log file to retrieve and save transaction information.
 * Time Complexity: O(n)
 * @param inputFile
 * @throws FileNotFoundException
 * @throws InterruptedException 
 */
private void readFile(File inputFile) throws FileNotFoundException {

        int transactionNumber = firstTransactionNumber;
        Scanner input = new Scanner(inputFile);

        while ( transactionNumber < finalTransactionNumber ) {

            String tempTime = input.next();
            String tempLine = input.nextLine();

            if ( !database.get(0).get(transactionNumber - firstTransactionNumber).equals("-1") && !database.get(1).get(transactionNumber - firstTransactionNumber).equals("-1") && !database.get(2).get(transactionNumber - firstTransactionNumber).equals("-1") && !database.get(3).get(transactionNumber - firstTransactionNumber).equals("-1")) {

                System.out.println("found transaction: " + transactionNumber);
                updateTransactionProgress();
                transactionNumber++;

            } else if ( tempLine.contains("StartTransaction") && tempLine.contains("#" + transactionNumber) ) {

                database.get(0).set(transactionNumber - firstTransactionNumber, tempTime);

            } else if ( tempLine.contains("EndTransaction") && tempLine.contains("#" + transactionNumber) ) {

                database.get(1).set(transactionNumber - firstTransactionNumber, tempTime);

            } else if ( tempLine.contains("FTransType=") && tempLine.contains("" + transactionNumber) ) {

                if ( database.get(2).get(transactionNumber - firstTransactionNumber).equals("-1") ) {

                    database.get(2).set(transactionNumber - firstTransactionNumber, (parseTransactionType(tempLine)));

                } else {

                    database.get(3).set(transactionNumber - firstTransactionNumber, (parseTransactionType(tempLine)));

                }

            } else if ( tempLine.contains("PrePayTrsNumber=") && tempLine.contains("#" + transactionNumber)) {

                database.get(4).set(transactionNumber - firstTransactionNumber, "Prepaid");

            } else if ( !input.hasNextLine() ) {

                if ( database.get(1).get(transactionNumber - firstTransactionNumber).equals("-1") && database.get(3).get(transactionNumber - firstTransactionNumber).equals("-1") ) {

                    database.get(1).set(transactionNumber - firstTransactionNumber, "");
                    database.get(3).set(transactionNumber - firstTransactionNumber, "");

                } else if ( database.get(3).get(transactionNumber - firstTransactionNumber).equals("-1") ) {

                    database.get(3).set(transactionNumber - firstTransactionNumber, "");

                }

                input.close();
                input = new Scanner(inputFile);
            }
        }

        input.close();
    }

/**
 * Creates and fills database ArrayList which holds individual ArrayLists for each type of transaction information.
 * Also instantiates variables which assist the log scanning process.
 * @param inputFile
 * @throws FileNotFoundException
 */
private void initializeArrayList( File inputFile ) throws FileNotFoundException {

    firstTransactionNumber = retrieveFirstTransactionNumber(inputFile);
    finalTransactionNumber = retrieveFinalTransactionNumber(inputFile, firstTransactionNumber);
    totalTransactions = (finalTransactionNumber - firstTransactionNumber);

    System.out.println(firstTransactionNumber);
    System.out.println(finalTransactionNumber);

    //database[0] = startTimes; database[1] = endTimes; database[2] = FTransType1; database[3] = FTransType2; database[4] = isPrepaid
    database = new ArrayList<ArrayList<String>>(5);

    for ( int i = 0; i < 5; i++ ) { database.add(new ArrayList<String>(totalTransactions)); }

    for ( int i = 0; i < totalTransactions; i++ ) {

        database.get(0).add("-1");
        database.get(1).add("-1");
        database.get(2).add("-1");
        database.get(3).add("-1");
        database.get(4).add("");

    }
}

/**
 * Parses strings from transaction log to retrieve transaction type.
 * @param tempLine
 * @return transaction type
 */
private String parseTransactionType(String tempLine) {

    if ( tempLine.contains("Sale")) { 

        return "Sale"; 

    } else if ( tempLine.contains("Void")) {

        return "Void";

    } else if ( tempLine.contains("PayOut")) {

        return "PayOut";

    } else if ( tempLine.contains("PayIn")) {

        return "PayIn";

    } else if ( tempLine.contains("Drop")) {

        return "Drop";

    } else if ( tempLine.contains("CloseBank")) {

        return "CloseBank";

    } else if ( tempLine.contains("OpenBank")) {

        return "OpenBank";

    } else if ( tempLine.contains("Refund")) {

        return "Refund";

    } else { return ""; }
}

/**
 * Writes transaction information withheld in the database ArrayList to output file.
 * @param outputFile
 * @throws FileNotFoundException
 */
private void writeTransactionInfo(File outputFile) throws FileNotFoundException {

    try {

        PrintWriter writer = new PrintWriter(new FileOutputStream(outputFile, false));
        writer.close();

        writer = new PrintWriter(new FileOutputStream(outputFile, true));

        for ( int i = 0; i < database.get(0).size(); i++ ) {

                writer.println(firstTransactionNumber + i + "," + database.get(0).get(i) + "," + database.get(1).get(i) + "," + database.get(2).get(i) + ","
                + database.get(3).get(i) + "," + database.get(4).get(i) + "," + storeInfo + "," + date);

        }

        writer.close();

    } catch (IOException e) {

        e.printStackTrace();

    }
}

/**
 * Used to initialize transaction number variable; Locates first transaction number used.
 * @param inputFile
 * @return transaction number
 * @return -1 if transaction number is not found
 * @throws FileNotFoundException
 */
private int retrieveFirstTransactionNumber(File inputFile) throws FileNotFoundException {

    Scanner input = new Scanner(inputFile);

    while ( input.hasNext() ) {

        String temp = input.next();

        if ( temp.contains("StartTransaction") ) {

            temp = input.next();

            if ( temp.equals("Trs") ) {

                temp = input.next();
                input.close();
                temp = temp.substring(1, temp.length());
                return Integer.parseInt(temp);

            }
        }
    }

    input.close();
    return -1;
}

/**
 * Returns last transaction number in log; used to determine when file reader should stop.
 * @param inputFile
 * @param firstTransactionNumber
 * @return -1 if not found
 * @throws FileNotFoundException
 */
private int retrieveFinalTransactionNumber(File inputFile, int firstTransactionNumber) throws FileNotFoundException {

    Scanner input = new Scanner(inputFile);
    String temp = null;
    int finalTransactionNumber = -1;

    while ( input.hasNextLine() ) {

        temp = input.nextLine();

        if ( temp.contains("#" + Integer.toString(firstTransactionNumber).substring(0, 1)) && temp.contains("StartTransaction")) {

            finalTransactionNumber = Integer.parseInt(temp.substring(87, 94));

        }
    }

    input.close();
    return finalTransactionNumber;
}

/**
 * Retrieves register ID and store number
 * @param inputFile
 * @return computerName
 * @throws FileNotFoundException
 */
private String retrieveStoreInfo(File inputFile) throws FileNotFoundException {

    Scanner input = new Scanner(inputFile);
    String computerName = null;

    while ( computerName == null && input.hasNext() ) {

        String temp = input.next();

        if ( temp.startsWith("SPR") ) { computerName = temp; }
    }

    input.close();
    return computerName;
}

/**
 * Retrieves date of log file
 * @param inputFile
 * @return date
 * @throws FileNotFoundException
 */
private String retrieveLogDate(File inputFile) throws FileNotFoundException {

    Scanner input = new Scanner(inputFile);
    String temp = null;

    while ( input.hasNext() ) {

        temp = input.next();

        if ( temp.contains("Date:")) {

            temp = input.next().replace('/', '-');
            input.close();
            return temp;

        }
    }

    input.close();
    return "";
}

}

每次调用“ updateTransactionProgress()”之后,当我检查progressBar的进度时,该值将正确更新。只是没有更新Interface类中的progressBar。

我是多线程新手,不确定我是否正确理解它。我查看了教程和其他示例,但没有找到太多帮助。我会很感激的。

编辑:包括整个类文件以获取更多详细信息。

2 个答案:

答案 0 :(得分:0)

您的Reader类似乎扩展了与您想像的类不同的类。您调用super.progressBar.setProgress(),它正在调用父类Interface的进度条来更改它。即使Interface扩展了GUI类,progressBar中的GUI还是私有的,因此您无法访问它。我建议您在实例化GUI时将进度条从Reader类传递到Reader

public class GUI extends Application {

    private ProgressBar _progressBar = new ProgressBar(0);

    // Methods which display GUI w/ progressBar

    private void runReader() {
      // progressBar should be instantiated by now, of course
      Reader reader = new Reader(_progressBar, "input Path", "output Path");
      Thread t = new Thread(reader, "reader thread");
      t.start();
    }
}

public class Reader extends Interface implements Runnable {
     private final ProgressBar _progressBar = bar;

     public void Reader(ProgressBar bar, String in, String out){
         _progressBar = bar;
         // Rest of constructor here
     }
    //Main processing methods that call 'updateTransactionProgress()'

    private void updateTransactionProgress() {

        if ( totalTransactions == 0 ) { super.pb.setProgress(0); }
        completedTransactions++;.

        // Call to Platform.runLater() to run the update on the JavaFX Application Thread
        Platform.runLater(() -> {
            _progressBar.setProgress((completedTransactions + 0.0) / 
            totalTransactions);
        });
    }
}

答案 1 :(得分:-2)

如果没有足够的信息来做出明智的猜测,建议您将Thread.sleep()添加到Reader线程的run()实现中。从您发布的内容来看,您的Reader线程可能正在消耗所有可用的处理器时间而没有休息,因此GUI线程永远不会有机会进入并更新显示。

如果添加Thread.sleep()解决了您发布的问题,则需要确定Reader线程的适当位置和持续时间,以使其在run()实现中定期让位。