2017-02-07 3 views
0

ファイルサイズが数千から数百万行になる大容量ファイルを1行ずつ解析しています。ファイルが少ない場合(例えば、10,000行が少ない場合)、すべてのコンポーネント(下の画像で赤い矢印で示されたコンポーネント)がすぐに1行ずつ更新され、プロセスがどこにあるのかを正確に示します。ファイルが大きい場合、更新に大きな遅延があり、インジケータが進捗状況を反映するまでに最大30秒かかることがあります。私はディスプレイをリフレッシュするためにそれを手動でサイズ変更することができました。Javafxの進捗状況が十分に速く更新されない

enter image description here

行毎にファイルがupdateProgress()ですが、ストリーミング時に呼び出されるメソッド。そこにはprogressPrevValが0から1になり(それぞれの増分は1.0/2,675,150)、lineNumは、この例を画像で使用すると1から2,675,150になります。 、

Runnable task =() -> { 
     DxProgressIndicator.main(null); 
    }; 
    Thread thread = new Thread(task); 
    thread.start(); 

私は、ファイルを介して行くことができます:私はこのような独自のスレッドでGUIを起動したら、

import javafx.application.Application; 
import javafx.application.Platform; 
import javafx.geometry.Insets; 
import javafx.geometry.Pos; 
import javafx.scene.Group; 
import javafx.scene.Scene; 
import javafx.scene.control.Label; 
import javafx.scene.control.ProgressBar; 
import javafx.scene.control.ProgressIndicator; 
import javafx.scene.control.TextField; 
import javafx.scene.layout.HBox; 
import javafx.scene.layout.VBox; 
import javafx.scene.text.Font; 
import javafx.scene.text.FontWeight; 
import javafx.stage.Stage; 

public class DxProgressIndicator extends Application { 

private static ProgressBar pb; 
private static ProgressIndicator pi; 
private static TextField tfNumbers; 
private static Label lblLineNumVal; 
private static Label lblAllLines; 
private static double progressIncrement; 
private static double progressPrevVal; 
private static int lineNum; 
private static Label lblFile; 

@Override 
public void start(Stage stage) { 
    Group root = new Group(); 
    Scene scene = new Scene(root); 
    stage.setScene(scene); 
    stage.setTitle("Dexter Parser"); 

    lblFile = new Label(); 
    final HBox hbFile = new HBox(); 
    hbFile.setAlignment(Pos.CENTER_LEFT); 
    Label lblCurrentFile = new Label("Current file: "); 
    lblCurrentFile.setMinWidth(90); 
    lblCurrentFile.setFont(Font.font(null, FontWeight.BOLD, 13)); 
    hbFile.getChildren().addAll(lblCurrentFile, lblFile); 

    pb = new ProgressBar(0); 
    pb.setMinWidth(450); 
    pi = new ProgressIndicator(0); 

    final HBox hbPis = new HBox(10); 
    hbPis.setAlignment(Pos.CENTER_LEFT); 
    hbPis.getChildren().addAll(pb, pi); 

    lblLineNumVal = new Label(); 
    lblLineNumVal.setMaxWidth(200); 

    Label slash = new Label("/"); 

    lblAllLines = new Label(); 

    final HBox hbLines = new HBox(); 
    hbLines.setAlignment(Pos.CENTER_LEFT); 
    Label lblLineNum = new Label(" Currently parsing Line # : "); 
    lblLineNum.setFont(Font.font(null, FontWeight.BOLD, 12)); 
    hbLines.getChildren().addAll(lblLineNum, lblLineNumVal, slash, lblAllLines); 

    tfNumbers = new TextField(); 
    tfNumbers.setEditable(false); 
    tfNumbers.setMaxWidth(100); 

    final HBox hbTrxValues = new HBox(); 
    hbTrxValues.setAlignment(Pos.CENTER_LEFT); 
    Label lblNumbers = new Label("(transaction, step, record) = "); 
    lblNumbers.setFont(Font.font(null, FontWeight.BOLD, 12)); 
    hbTrxValues.getChildren().addAll(lblNumbers, tfNumbers); 

    final VBox vb = new VBox(10); 
    vb.getChildren().addAll(hbFile, hbPis, hbLines, hbTrxValues); 
    vb.setPadding(new Insets(10)); 

    scene.setRoot(vb); 
    stage.show(); 
} 

public static void updateProgress() { 
    Platform.runLater(() -> { 
     progressPrevVal += progressIncrement; 
     pb.setProgress(progressPrevVal); 
     pi.setProgress(progressPrevVal); 
     lblLineNumVal.setText(Integer.toString(lineNum++)); 
    }); 
} 

public static void setFileMetadata(String str) { 
    Platform.runLater(() -> { 
     lblAllLines.setText(str); 
     progressIncrement = 1/Double.valueOf(str); 
     progressPrevVal = 0d; 
     lineNum = 1; 
    });  
} 

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

クライアントのmainメソッドから:ここで

は、全体のコードです

try (Stream<String> stream = Files.lines(file)) { 
     stream.forEach(line -> { 
      DxProgressIndicator.updateProgress(); 
      // a bunch of stuff to do with the line 
     }); 
    } catch (IOException e) { 
    } 

私は何が欠けていますか?非常に大きなファイルを処理する場合、コンポーネントを1分ごとに更新するのではなく、繰り返しごとにスムーズに更新できるように、更新を強制するにはどうすればよいですか?

ありがとうございます。

+1

FXアプリケーションスレッドには、それほど多くの更新情報が氾濫している可能性があります。 JavaFX ['Task'](http://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html)を使用して、インジケータの進行状況プロパティをタスクのprogressプロパティにバインドし、 'updateProgress()'を呼び出すと、更新の数が制限されます。 (実際の答えを提供するためにあなたのコードを解くことができない理由)UIクラスの外からこれを運転する理由は明らかではありません。通常は、 'start ) 'メソッドを呼び出します。 –

+0

2つ目は、ファイルを1行ずつ順番に処理する多くのプロジェクトを構築する必要がありますが、最終的にはさまざまなことを行うことになります。このようにして、私はここで同じアプリケーション(つまり 'updateProgress()'メソッド)を使用して、ファイルをストリームするプロジェクトを扱うことができます。 – mohsenmadi

+0

しかし、同じUI構造を再利用する明確な方法があります。人為的にすべてを静的にする必要はありません。たとえば、このアプローチでは、これらの2つを一度に開くことができなくなります。後で簡単に問題になると思います。 –

答えて

1

Taskを使用し、Platform.runLater()への処理を進めるために、FXアプリケーションスレッドに実行する更新が多すぎないように、進捗状況を更新するにはupdateProgress()を呼び出します。

だから、あなたが行うことができます:

public class ParseTask extends Task<Void> { 

    private final Path file ; 
    private long totalLines ; 

    public ParseTask(...) { 
     file = ... ; 
     totalLines = ... ; 
    } 

    public Void call() throws IOException { 

     // better to use AtomicLong here in case you parallelize the parsing 
     // at any point... 
     AtomicLong linesRead = new AtomicLong() ; 

     try (Stream<String> stream = Files.lines(file)) { 
      stream.forEach(line -> { 
       updateProgress(linesRead.incrementAndGet(), totalLines); 
       // do stuff with line... 
      }); 
     } 

     return null ; 
    } 

    public long getTotalLines() { 
     return totalLines ; 
    } 
} 

その後、

public class DxProgressIndicator { 

    private final VBox vb ; 
    private ProgressBar pb; 
    private ProgressIndicator pi; 
    private TextField tfNumbers; 
    private Label lblLineNumVal; 
    private Label lblAllLines; 
    private double progressIncrement; 
    private double progressPrevVal; 
    private int lineNum; 
    private Label lblFile; 

    private final DoubleProperty progress = new SimpleDoubleProperty(); 

    public DoubleProperty progressProperty() { 
     return progress ; 
    } 

    public final double getProgress() { 
     return progressProperty().get(); 
    } 

    public final void setProgress(double progress) { 
     progressProperty().set(progress); 
    } 

    public DxProgressIndicator() { 

     lblFile = new Label(); 
     final HBox hbFile = new HBox(); 
     hbFile.setAlignment(Pos.CENTER_LEFT); 
     Label lblCurrentFile = new Label("Current file: "); 
     lblCurrentFile.setMinWidth(90); 
     lblCurrentFile.setFont(Font.font(null, FontWeight.BOLD, 13)); 
     hbFile.getChildren().addAll(lblCurrentFile, lblFile); 

     pb = new ProgressBar(0); 
     pb.setMinWidth(450); 

     pi = new ProgressIndicator(0); 

     pb.progressProperty().bind(progress); 
     pi.progressProperty().bind(progress); 

     final HBox hbPis = new HBox(10); 
     hbPis.setAlignment(Pos.CENTER_LEFT); 
     hbPis.getChildren().addAll(pb, pi); 

     lblLineNumVal = new Label(); 
     lblLineNumVal.setMaxWidth(200); 

     Label slash = new Label("/"); 

     lblAllLines = new Label(); 

     final HBox hbLines = new HBox(); 
     hbLines.setAlignment(Pos.CENTER_LEFT); 
     Label lblLineNum = new Label(" Currently parsing Line # : "); 
     lblLineNum.setFont(Font.font(null, FontWeight.BOLD, 12)); 
     hbLines.getChildren().addAll(lblLineNum, lblLineNumVal, slash, lblAllLines); 

     tfNumbers = new TextField(); 
     tfNumbers.setEditable(false); 
     tfNumbers.setMaxWidth(100); 

     final HBox hbTrxValues = new HBox(); 
     hbTrxValues.setAlignment(Pos.CENTER_LEFT); 
     Label lblNumbers = new Label("(transaction, step, record) = "); 
     lblNumbers.setFont(Font.font(null, FontWeight.BOLD, 12)); 
     hbTrxValues.getChildren().addAll(lblNumbers, tfNumbers); 

     vb = new VBox(10); 
     vb.getChildren().addAll(hbFile, hbPis, hbLines, hbTrxValues); 
     vb.setPadding(new Insets(10)); 

    } 

    public Parent getRoot() { 
     return vb ; 
    } 

    public void setTotalLines(long totalLines) { 
     lblAllLines.setText(Long.toString(totalLines)); 
    } 

} 

そしてちょうど

public class MyApp extends Application { 

    @Override 
    public void start(Stage primaryStage) { 

     ParseTask task = new ParseTask(...); 

     DxProgressIndicator indicator = new DxProgressIndicator(); 
     indicator.setTotalLines(task.getTotalLines()); 
     indicator.progressProperty().bind(task.progressProperty()); 

     Scene scene = new Scene(indicator.getRoot()); 
     primaryStage.setScene(scene); 
     primaryStage.setTitle("Dexter Parser"); 

     primaryStage.show(); 

     new Thread(task).start(); 
    } 
} 

明らかにあなたが詳細については、これをいじくり回す必要があるかもしれませんが - 例えばupdateMessage(linesRead + "/" + totalLines);を実行し、ラベルのテキストをタスクのmessageProperty()にバインドすることができますが、それはあなたに基本的な考えを与えるはずです。

+0

多くの感謝@ James_D。私はこれを組み込み、報告する。 – mohsenmadi

+0

また、Jamesは感謝しています。私は自分のアプリケーションをJavaFXアプリケーションにしたくないから、必要に応じてアップデートしてGUIを呼び出すだけでしたが、何とか巻き返していることが分かりました。私はあなたの方法を適用し、それは非常にうまく動作します。ありがとう。 – mohsenmadi

+0

私は質問があります。 'MyApp'が現在セットアップされている方法は、一度に1ファイルしか処理できません。 'ParseTask'の' call() 'で、ファイルのディレクトリを繰り返し処理する必要があります。私はこのアプローチを実装した後に立ち往生した。私はいくつかのスレッドを管理する必要があると思う。もしあなたが助けることができたら、私はあなたの答えをはっきりと見て新しい投稿をすることができます。ありがとうございました。 – mohsenmadi

1

javaFXタスククラスはアトミックな参照を使用して、イベントキューにあふれないように更新を統合します(これにより問題が発生します)。このメカニックをコードに簡単に実装できます。

最初に、ヘルパークラスが必要になります。

private static final class ProgressUpdate { 
    private final double workDone;   

    private ProgressUpdate(double p) { 
      this.workDone = p;    
    } 
} 

その後、あなたは、このクラスのオブジェクトへの原子の参照を格納するフィールドが必要です。

private static final AtomicReference<ProgressUpdate> progressUpdate = new AtomicReference<>(); 

をし、最終的にあなたは、このようなあなたのupdateProgress()を適応させることができます:

public static void updateProgress() { 
    progressPrevVal += progressIncrement; 
    if(progressUpdate.getAndSet(new ProgressUpdate(progressPrevVal)) == null){  
     Platform.runLater(() -> { 
      ProgressUpdate update = progressUpdate.getAndSet(null); 
      pb.setProgress(update.workDone); 
      pi.setProgress(update.workDone); 
      lblLineNumVal.setText(Integer.toString(lineNum++)); 
     }); 
    } 
} 

このようにして、新しい更新がt GUIが既に前のものを消費した場合は、FXアプリケーションスレッド。

しかし、javaFXがどのように内部的にこの問題を処理しているのかを知り、組み込みのTask javaFXクラスを使用できるようにコードを再設計することをお勧めします。あなたの現在の、そしておそらく数多くの次の問題を自動的に解決します。

+0

あなたの時間のための電卓をありがとう。私はあなたの解決策を試しましたが、 'progressUpdate'が' workDone'オブジェクトについて不平を言っていました!私は何かが欠けていたか? – mohsenmadi

+0

@mohsenmadiそれは私のコードのタイプミスでした。フィールドは更新オブジェクトに属します。 – Calculator

+0

ありがとう@計算機。あなたのメソッドは私がすでに書いたプロジェクトでうまくいき、以下のJamesのアプローチを使って書き直したいとは思わなかった。とても有難い。 – mohsenmadi

関連する問題