Vala强制刷新进度条

时间:2017-09-26 19:42:59

标签: gtk progress-bar vala

我已经用vala做了一个应用,在某些时候我必须处理很多文件。我已经创建了一个窗口来选择一个文件夹,然后我得到了文件的路径并对它们进行了一些处理。

我在此窗口中添加了一个进度条,以显示已处理的文件数,但由于某种原因,它始终为空。 关于窗口的代码:

checks.AddFullMicroserviceIncludingDatabaseAndUrlCheck(Configuration["OrderingUrl"])

正如您所看到的,我将“select”按钮连接到“do_stuff”方法,在那里我获取所选文件的路径并进行一些处理。

我正确更新了progre bar的分数,因为我添加了一些打印件来知道值是否正确而且确实如此。只是窗口没有刷新,可能是因为它正在处理文件的所有工作。这是关于do_stuff()方法的代码:

        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 ();

printf显示正在更新progres栏的值,但在窗口中栏始终为空。

我错过了什么吗?这是进行进度条的正确方法吗?我应该制作另一个线程吗?

2 个答案:

答案 0 :(得分:1)

除非您没有显示某些相关代码,否则您将屏蔽main loop。一种选择是在线程中执行所有操作,并使用空闲回调来更新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整洁,但你真的不需要。

答案 1 :(得分:1)

正如@nemequ所说,你的代码阻塞了主循环线程(它处理用户输入和调度/绘制窗口小部件更新),因此在方法完成之前不会更新进度条。

使用线程是解决问题的一种方法,但是使用线程可能会导致很多错误,因为线程之间的简单交互很难安全。

异步方法通过将代码与主循环完成的其他工作交错来避免这种情况。您do_stuff()的异步版本非常简单,只需将其声明为async并在for循环中放置一个yield:

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.begin()

从点击处理程序中将其踢出