2017-09-26 2 views
0

私はいくつかの点で私は多くのファイルを処理する必要があるバラとのアプリケーションを作った。私は、フォルダを選択するためのウィンドウを作成した後、ファイルのパスを取得し、いくつかのprocesを作成します。ヴァルフォースリフレッシュプログレスバー

このウィンドウに進捗バーを追加して、処理されたファイルの数を表示しましたが、なんらかの理由で常に空のままです。ウィンドウについて コード:あなたが見ることができるように

 this.files_window = new Gtk.Window(); 
     this.files_window.window_position = Gtk.WindowPosition.CENTER; 
     this.files_window.destroy.connect (Gtk.main_quit); 
     // VBox: 
     Gtk.Box vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); 
     this.files_window.add (vbox); 
     // Buttons to open and close 
     Gtk.Button cancel = new Gtk.Button.with_label ("Cancel"); 
     Gtk.Button select = new Gtk.Button.with_label ("Select"); 
     vbox.add (select); 
     vbox.add (cancel); 
     // proogress bar 
     this.progress_bar = new Gtk.ProgressBar(); 
     vbox.add(this.progress_bar); 
     // conect select to method do_stuff 
     select.clicked.connect (do_stuff); 
     this.files_window.show_all(); 

、私は私が選択したファイルのパスを取得し、いくつかのプロセスを作る方法「do_stuff」に「選択」ボタンを接続します。

私は値が正しいかどうかを知るためにいくつかのプリントを追加したので、プログラムバーの部分を正しく修正します。それは、ウィンドウがリフレッシュしていないということだけです。おそらく、ファイルの処理ですべての作業が行われているからです。

 // some proces to get paths of files in the list sfiles 
     double fraction = 0.0; 
     this.progress_bar.set_fraction (fraction); 
     int processed_files = 0; 
     foreach (string sfile in sfiles) { 
      do_some_proces_to_file(sfile); 
      processed_files += 1; 
      fraction = (double)processed_files/(double)sfiles.length; 
      this.progress_bar.set_fraction (fraction); 
      stdout.printf("Real fraction: %f\n", this.progress_bar.get_fraction()); 
     } 

のprintfはプログレバーの値が更新されていることを示しているが、ウィンドウにバーが常に空である:ここでdo_stuff()メソッドのコードがあります。

何か不足していますか?それはプログレバーを行う正しい方法ですか?私はものをする別のスレッドを作ったか?

答えて

1

@nemequは、あなたのコードはメインループスレッド(ユーザー入力とスケジューリング/描画ウィジェットの更新の両方を処理する)をブロックしているので、メソッドが完了するまで進行状況バーは更新されません。

スレッドを使用することで問題は解決されますが、スレッドを使用すると多くのバグが発生する可能性がありますが、スレッド間の単純なやりとりを安全にすることは困難な場合があります。

非同期メソッドは、コードをメインループによって実行されている他の作業とインターリーブすることによってこれを回避します。

public async void do_stuff() { 
    ... 
    foreach (string sfile in sfiles) { 
     // all of this is as before 
     do_some_proces_to_file(sfile); 
     processed_files += 1; 
     fraction = (double)processed_files/(double)sfiles.length; 
     this.progress_bar.set_fraction (fraction); 

     // Schedule the method to resume when idle, then 
     // yield control back to the caller 
     Idle.add(do_stuff.callback); 
     yield; 
    } 
} 

あなたはその後呼び出すことで、あなたのクリックハンドラからそれをキックオフすることができます:あなたのdo_stuff()の非同期バージョンは、単にそれが非同期宣言し、どこかのforループの歩留まりを入れて、書くことはかなりストレートフォワードだろうdo_stuff.begin()

+0

ありがとうございます!あなたのソリューションは完璧に機能しました。そして、あなたが言うように、私はスレッドで作業するのではなく、これを行う方が好きです。 – bcedu

1

あなたが表示していない関連コードがない限り、あなたはmain loopをブロックしています。 1つのオプションはスレッド内のすべてを行い、アイドル状態のコールバックを使用してUIを更新することです。基本的な考え方は次のようなものです:

new GLib.Thread<void*>("file-processor",() => { 
    foreach (string sfile in sfiles) { 
    /* do stuff */ 
    GLib.Idle.add(() => { 
     /* Update progress */ 
     return false; 
    }); 
    } 
    return null; 
}); 

アプリケーションによっては、競合状態を避けるためにミューテックスを追加する必要があります。また、操作を取り消すロジックを追加する必要があるかもしれません。

より良いオプションはGLib.ThreadPoolです。アイドル状態のコールバックからUIを更新したいと思うかもしれませんが、これにより各タスクを並行して実行できるようになり、大幅なスピードアップが可能になります。

もし私があなただったら、async functionにまとめてAPIをきれいに保つことができますが、本当にそうする必要はありません。