如何在不阻塞 UI 的情况下通过长时间操作更新 Vala 中的 GTK+ UI
How can one update GTK+ UI in Vala from a long operation without blocking the UI
当我使用此页面中的任何代码而不进行任何修改时:https://wiki.gnome.org/Projects/Vala/AsyncSamples
我总是得到:
warning: ‘g_simple_async_result_new’ is deprecated: Use 'g_task_new' instead.
所以我继续使用 GTask 的建议。但是,当我尝试在 Vala 中使用 GLib.Task 时,我在声明任务时卡住了。因此,我没有在自己的代码中使用来自 GIO 的异步,因为它已被弃用,我尝试使用 GLib.Task 来简单地使用 for 循环中的数字更新 Gtk 按钮的标签,这样代码如下所示:
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();
}
}
但是在编译时我得到:error: ‘_data_’ undeclared (first use in this function)
_tmp3_ = g_task_new_finish (_data_->_res_);
第 15 行是导致编译器抛出该错误的原因。它来自vala编译器生成的C代码。
我发现的主要问题是 Vala 中的 GTask 构造函数签名与 C 不同。因此,我无法重新创建此处找到的代码:
因为对于初学者来说,我不允许将两个以上的参数传递给 GLib.Task 对象构造函数。 Task 对象的构造函数在每种语言中都不同。 Vala 中 GLib.Task 的构造函数可以在 here.
中找到
因此我的问题是:
是否有任何示例说明如何在 Vala 中使用 GLib 任务 (GTask) 来执行更新 UI 的操作而不阻塞它?如果没有,是否有另一种方法可以更新 UI 而不会阻塞它?一种不被弃用的方式?
谢谢。
P.S:我已经尝试过 GLib.Thread、GLib.ThreadPool 和 GLib.Idle。它们都在 for 循环中阻塞了 UI。 GLib.Idle 不会完全阻止 UI,但它会在某种意义上呈现错误,即在循环 运行.
时响应用户输入变得非常慢
使用 async 非常好,some work already 移植当前代码以使用 GTask。
您的计数代码是阻塞的,所以即使它的执行被 GTask 缓冲,它仍然会阻塞 UI。
执行 CPU 密集后台操作的正确方法要么使用异步子进程,要么在线程中启动工作并在主循环中分派。
async void test_async () {
new Thread<void> (() => {
// count here...
test_async.callback ();
});
yield;
}
GTask 或更一般的 GAsyncResult 只提供一个容器来保存异步操作的结果。他们还建议使用 GThreadPool,但样板文件有点多。
另一个有趣的事情是 test_async.callback
实际上是一个 SourceFunc
,所以你可以在 GLib.Timeout
中传递它。
编辑:
为了更好地解决您的问题,如果您想在 UI 进行时更新它,请使用异步循环:
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
}
}
这是一个完整的工作示例:
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://wiki.gnome.org/Projects/Vala/AsyncSamples
我总是得到:
warning: ‘g_simple_async_result_new’ is deprecated: Use 'g_task_new' instead.
所以我继续使用 GTask 的建议。但是,当我尝试在 Vala 中使用 GLib.Task 时,我在声明任务时卡住了。因此,我没有在自己的代码中使用来自 GIO 的异步,因为它已被弃用,我尝试使用 GLib.Task 来简单地使用 for 循环中的数字更新 Gtk 按钮的标签,这样代码如下所示:
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();
}
}
但是在编译时我得到:error: ‘_data_’ undeclared (first use in this function)
_tmp3_ = g_task_new_finish (_data_->_res_);
第 15 行是导致编译器抛出该错误的原因。它来自vala编译器生成的C代码。
我发现的主要问题是 Vala 中的 GTask 构造函数签名与 C 不同。因此,我无法重新创建此处找到的代码:
因为对于初学者来说,我不允许将两个以上的参数传递给 GLib.Task 对象构造函数。 Task 对象的构造函数在每种语言中都不同。 Vala 中 GLib.Task 的构造函数可以在 here.
中找到因此我的问题是:
是否有任何示例说明如何在 Vala 中使用 GLib 任务 (GTask) 来执行更新 UI 的操作而不阻塞它?如果没有,是否有另一种方法可以更新 UI 而不会阻塞它?一种不被弃用的方式?
谢谢。
P.S:我已经尝试过 GLib.Thread、GLib.ThreadPool 和 GLib.Idle。它们都在 for 循环中阻塞了 UI。 GLib.Idle 不会完全阻止 UI,但它会在某种意义上呈现错误,即在循环 运行.
时响应用户输入变得非常慢使用 async 非常好,some work already 移植当前代码以使用 GTask。
您的计数代码是阻塞的,所以即使它的执行被 GTask 缓冲,它仍然会阻塞 UI。
执行 CPU 密集后台操作的正确方法要么使用异步子进程,要么在线程中启动工作并在主循环中分派。
async void test_async () {
new Thread<void> (() => {
// count here...
test_async.callback ();
});
yield;
}
GTask 或更一般的 GAsyncResult 只提供一个容器来保存异步操作的结果。他们还建议使用 GThreadPool,但样板文件有点多。
另一个有趣的事情是 test_async.callback
实际上是一个 SourceFunc
,所以你可以在 GLib.Timeout
中传递它。
编辑:
为了更好地解决您的问题,如果您想在 UI 进行时更新它,请使用异步循环:
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
}
}
这是一个完整的工作示例:
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;
}
}