Rust 第二个gtk_main_quit()不工作

Rust 第二个gtk_main_quit()不工作,rust,gtk,Rust,Gtk,在应用程序中,我嵌套gtk事件循环,以便能够在回调中返回值。 但是,我对已经调用gtk\u main\u quit()的回调有一些问题,因为对该函数的多次调用似乎没有退出那么多的事件循环嵌套 以下是我的问题的一个例子: extern crate gtk; use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::thread; use gtk::{Butto

在应用程序中,我嵌套gtk事件循环,以便能够在回调中返回值。 但是,我对已经调用
gtk\u main\u 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()的两次调用之间,我应该做什么
停止事件循环的两个嵌套?

我已通过手动创建glib
MainLoop
并使用它代替嵌套调用gtk事件循环来解决问题。

我已通过手动创建glib
MainLoop
并使用它代替嵌套调用gtk事件循环来解决问题。

简单回答:解决方案是将第二个
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\u add
持续检查标志将导致忙循环,这几乎是您想要避免的。(在我的机器上,运行程序需要一个完整的CPU核心。)一般来说,首选的方法是等待条件变量。但是,如果您只需要告诉GUI线程做一些事情,如代码所示,您可以从另一个线程调用
glib::idle\u 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();
}

简短回答:解决方案是将第二个
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\u add
持续检查标志将导致忙循环,这几乎是您想要避免的。(在我的机器上,运行程序需要一个完整的CPU核心。)一般来说,首选的方法是等待条件变量。但是,如果您只需要告诉GUI线程做一些事情,如代码所示,您可以从另一个线程调用
glib::idle\u 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();
}

连续调用
gtk_main_quit()
两次都没有效果,因为只有最里面的
gtk_main()
会被标记为退出,而它只是被标记为退出,所以您需要返回到
gtk_main()
才能真正退出。因此,使第二个
gtk_main_quit()
退出的唯一方法是将其置于内部
gtk_main()
返回之后。然而,我不完全理解你的代码的目标是什么,特别是因为你使用
gtk_main()
gtk::idle_add()
是不寻常的,所以我觉得我还不能正确回答这个问题。你想做什么?那实际上只是个例子。我真正的代码非常复杂,所以我没有发布它。我试图实现的是在窗口关闭时询问用户是否退出。这是一个嵌套的事件循环,因为如果用户不想退出,我需要阻止关闭窗口。如果用户想退出,
gtk::main_quit()
会被调用两次(不是像本问题中那样的一行中有两条指令,但效果是一样的)。在这样的一行中调用
gtk_main_quit()
两次没有效果,因为只有最里面的
gtk_main()
将被标记为退出-它只是被标记为退出,因此您需要返回到
gtk_main()
才能真正退出。因此,使第二个
gtk_main_quit()
退出的唯一方法是将其置于内部
gtk_main()
返回之后。然而,我不完全理解你的代码的目标是什么,特别是因为你使用
gtk_main()
gtk::idle_add()
是不寻常的,所以我觉得我还不能正确回答这个问题。你想做什么?那实际上只是个例子。我真正的代码非常复杂,所以我没有发布它。我试图实现的是在窗口关闭时询问用户是否退出。这是一个嵌套的事件循环,因为如果用户不想退出,我需要阻止关闭窗口。如果用户想要退出,
gtk::main_quit()
将被调用两次(不是像本问题中那样一行两条指令,但效果相同)。