How can one update GTK+ UI in Vala from a long operation without blocking the UI

余生颓废 提交于 2019-12-12 10:25:23

问题


When I use any of the codes in this page without modifying anything: https://wiki.gnome.org/Projects/Vala/AsyncSamples

I always get: warning: ‘g_simple_async_result_new’ is deprecated: Use 'g_task_new' instead.

So I proceed with the recommendation of using GTask. However, when I try to use GLib.Task in Vala, I get stuck just declaring a task. So instead of using async from GIO in my own code, since it is deprecated, I try to use GLib.Task to simply update the label of a Gtk Button with numbers from a for loop, such that the code looks like this:

using Gtk;
Button button;

public static int main (string[] args) {
    Gtk.init (ref args);

    var window = new Window ();
    window.title = "Count without blocking the UI";
    window.border_width = 10;
    window.window_position = WindowPosition.CENTER;
    window.set_default_size (350, 70);
    window.destroy.connect (Gtk.main_quit);
    button = new Button.with_label ("Start counting");
    button.clicked.connect (() => {
    GLib.Task task = new GLib.Task(button, new Cancellable());
});

window.add (button);
window.show_all ();

Gtk.main ();

return 0;
}

void count(){
    for(int i = 0; i < 10000; i++){
        button.label = i.to_string();
    }
}

But when compiling I get: error: ‘_data_’ undeclared (first use in this function) _tmp3_ = g_task_new_finish (_data_->_res_);

The line number 15 is what is causing the compiler to throw that error. It comes from the C code that the vala compiler generates.

The main problem I found is that the GTask constructor signatures in Vala are different from C. Therefore, I could not re-create the code found here: GUI becomes unresponsive after clicking the button using GTK+ in C

Because for starters, I am not allowed to pass more than two arguments to the GLib.Task object constructor. The constructors of the Task object are different in each language. The constructor for GLib.Task in Vala can be found here.

Hence my question:

Are there any examples on how to use GLib Task (GTask) in Vala to perform an operation that updates the UI without blocking it? If not, is there another way to update the UI without blocking it? A way that is not deprecated?

Thanks.

P.S: I have already tried GLib.Thread, GLib.ThreadPool, and GLib.Idle. They all block the UI while in the for loop. GLib.Idle does not block the UI completely, but it renders it buggy in the sense that it becomes really slow to respond to user input while the loop is running.


回答1:


It's perfectly fine to use async and there's some work already for porting the current code to use GTask.

Your counting code is blocking, so even if its execution is cushioned with GTask, it will still block the UI.

The correct way of performing CPU intensive background operations either use subprocess asynchronously or launch the work in a thread and dispatch in the main loop.

async void test_async () {
    new Thread<void> (() => {
         // count here...
        test_async.callback ();
    });
    yield;
}

The GTask or more generally GAsyncResult only provide a container for holding the result of an asynchronous operation. They also recommend to use a GThreadPool, but it's a bit more boilerplate.

Another interesting thing is that test_async.callback is actually a SourceFunc, so you can pass it around in GLib.Timeout.

EDIT:

To fit more your question, if you want to update the UI while it progress, use an async loop:

async test_callback () {
    for (var i = 0; i < 10000; i++) {
        button.label = i.to_string ();
        Idle.add (test_async.callback);
        yield; // pause execution until retriggered in idle
    }
}

Here's a full and working example:

using Gtk;
Button button;

public static int main (string[] args) {
    Gtk.init (ref args);

    var window = new Window ();
    window.title = "Count without blocking the UI";
    window.border_width = 10;
    window.window_position = WindowPosition.CENTER;
    window.set_default_size (350, 70);
    window.destroy.connect (Gtk.main_quit);
    button = new Button.with_label ("Start counting");
    button.clicked.connect (() => {
        count ();
    });

    window.add (button);
    window.show_all ();

    Gtk.main ();

    return 0;
}

async void count(){
    for(int i = 0; i < 10000; i++){
        button.label = i.to_string();
        Idle.add (count.callback);
        yield;
    }
}


来源:https://stackoverflow.com/questions/39502200/how-can-one-update-gtk-ui-in-vala-from-a-long-operation-without-blocking-the-ui

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!