在应用程序中,我嵌套了gtk事件循环,以便能够在回调中返回一个值。
但是,我在调用gtk_main_quit()
时遇到了一些问题,因为多次调用此函数似乎没有退出事件循环的多个嵌套。
以下是我的问题的一个例子:
extern crate gtk;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::thread;
use gtk::{Button, ButtonExt, ContainerExt, Continue, Inhibit, WidgetExt, Window, WindowType};
fn main() {
gtk::init().unwrap();
let window = Window::new(WindowType::Toplevel);
let quit = Arc::new(AtomicBool::new(false));
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(false)
});
let button = Button::new_with_label("Click");
let quit2 = quit.clone();
button.connect_clicked(move |_| {
let quit = quit2.clone();
thread::spawn(move || {
quit.store(true, Ordering::Relaxed);
});
println!("Run");
gtk::main();
});
window.add(&button);
window.show_all();
gtk::idle_add(move || {
if quit.load(Ordering::Relaxed) {
println!("Quit");
gtk::main_quit();
gtk::main_quit();
quit.store(false, Ordering::Relaxed);
}
Continue(true)
});
gtk::main();
}
正如您在gtk::idle_add
调用中看到的那样,我调用了gtk::main_quit()
两次,当按下按钮时应退出应用程序,因为gtk::main()
也被调用两次(最后一次) main
函数,按钮中的另一个单击回调)。
但是当我点击按钮时,应用程序不会退出。
gtk的文档似乎表明这是预期的行为:
使主循环的最内部调用在重新获得控制时返回 。
(重点是我的)
所以,我认为这不会退出应用程序,因为调用gtk::main_quit()
两次不会允许gtk主循环“重新获得控制权”。
我的问题是,在gtk::main_quit()
的两次调用之间我应该怎么做才能停止事件循环的2次嵌套?
答案 0 :(得分:2)
简短回答:解决方案是将第二个gtk::main_quit
替换为:
gtk::idle_add(|| {
gtk::main_quit();
Continue(false)
});
和以前一样,第一个gtk::main_quit()
将安排内部主循环退出。此外,空闲处理程序将被外部主循环拾取,导致它立即终止。
这可以通过伪递归函数进行推广,该函数可以根据需要多次重复该过程:
fn deep_main_quit(n: usize) {
if n == 0 {
return;
}
gtk::main_quit();
gtk::idle_add(move || {
deep_main_quit(n - 1);
Continue(false)
});
}
请注意,使用idle_add
来持续检查标志将导致忙碌循环,您几乎肯定希望避免这种情况。 (在我的机器上,运行程序会占用一个完整的CPU内核。)通常,首选方法是等待条件变量。但是如果你只需要告诉GUI线程做某事,如代码所示,你可以从另一个线程调用glib::idle_add
。提供的回调将在GUI线程中排队并执行。
通过此更改,在GUI线程中释放两个级别gtk_main
的一个线程将如下所示:
fn main() {
gtk::init().unwrap();
let window = Window::new(WindowType::Toplevel);
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(false)
});
let button = Button::new_with_label("Click");
button.connect_clicked(|_| {
thread::spawn(|| {
glib::idle_add(|| {
println!("Quit");
deep_main_quit(2);
Continue(false)
});
});
println!("Run");
gtk::main();
});
window.add(&button);
window.show_all();
gtk::main();
}
答案 1 :(得分:0)
我通过手动创建一个glib MainLoop
并使用它而不是嵌套调用gtk事件循环来解决我的问题。