3个不稳定版本
| 0.4.1 | 2020年12月12日 | 
|---|---|
| 0.4.0 | 2020年11月22日 | 
| 0.3.0 | 2020年1月25日 | 
在GUI中排名#369
每月下载量34次
在flowide中使用
21KB
153 行
gtk-rs-state
在稳定版上运行。
这不是最快的实现,但对于几乎所有用例来说应该足够了。
Cargo.toml
[dependencies]
gtk-rs-state = "0.3"
示例用法
使用这个库归结为
- 调用gtk_refs!宏。
- 在启动事件循环之前调用init_storage()。
- 在其他线程中调用do_in_gtk_eventloop()来修改小部件。
不要在主线程中调用do_in_gtk_eventloop(),因为这会阻塞。
    gtk_refs!(
        pub mod widgets;                // The macro emits a new module with this name
        struct WidgetRefs;              // The macro emits a struct with this name containing:
        main_window : gtk::Window ,     // widget_name : Widgettype
        button1 : gtk::Button           // ..
    );
    fn main() {
        if gtk::init().is_err() {
            println!("Failed to initialize GTK.");
            return;
        }
        let window = Window::new(WindowType::Toplevel);
        window.set_title("gtk-rs-state Example Program");
        window.set_default_size(350, 70);
        let button = Button::new_with_label("Spawn another thread!");
        window.add(&button);
        window.show_all();
        window.connect_delete_event(|_, _| {
            gtk::main_quit();
            Inhibit(false)
        });
        button.connect_clicked(|_| {
            std::thread::spawn(some_workfunction);
            println!("Clicked!");
        });
        // You need the following two statements to prepare the
        // static storage needed for cross thread access.
        // See the `from_glade.rs` example for a more elegant solution
        let widget_references = widgets::WidgetRefs {
            main_window: window.clone(),
            button1:     button.clone(),
        };
        widgets::init_storage(widget_references);
        // End
        // This type has a function for each of your widgets.
        // These functions return a clone() of the widget.
        window.show_all();
        window.connect_delete_event(move |_, _| {
            gtk::main_quit();
            Inhibit(false)
        });
        // Start event loop
        gtk::main();
    }
    fn compute() {
        use std::thread::sleep;
        use std::time::Duration;
        sleep(Duration::from_secs(1));
    }
    fn some_workfunction()  {
        let mut i = 0;
        loop {
            compute();
            i += 1;
            let text = format!("Round {} in {:?}", i, std::thread::current().id());
            widgets::do_in_gtk_eventloop(|refs| {
                refs.button1().set_label(&text);
            });
        }
    }
该宏生成以下代码
    pub mod widgets {
        pub struct WidgetRefs {
            pub main_window : gtk::Window,
            ...
        }
        impl From<>k::Builder> for WidgetRefs { ... };
        impl WidgetRefs {
            fn main_window() -> gtk::Window { } // returns a .clone() of the widget
            ...
        }
        pub fn init_storage(WidgetRefs);
        pub fn init_storage_from_builder(>k::Builder);
        pub fn do_in_gtk_eventloop( FnOnce(Rc<WidgetRefs>) );
    }
它是如何工作的?
- 您提供的闭包通过glib::idle_add()在GTK事件循环中执行。
- 在调用点do_in_gtk_eventloop()会等待闭包运行。
- 来自多个线程的闭包将始终顺序执行。
- 如果闭包崩溃,do_in_gtk_eventloop()也会崩溃。您可能看不到崩溃,因为进程通常退出得太快。
如果您想: - 在结构体中使用额外的非发送字段(除了小部件引用以外的其他内容) - 使用glade,请参阅示例文件夹
实现是如何工作的?
+---------------------------------+   +----------------------------------+
|GTK event loop thread            |   |Global statics                    |
|                                 |   |                                  |
|  +----------------------------+ |   |  TX : Sender<(Fn, Cb)>           |
|  |Thread local statics        | |   |                                  |
|  |                            | |   |                                  |
|  |  DATA : Non-Send Refereces | |   +----------------------------------+
|  |  RX : Receiver<(Fn, Cb)>   | |
|  |                            | |   +----------------------------------+
|  +----------------------------+ |   |Some other thread                 |
|                                 |   |                                  |
|                                 |   |  do some stuff                   |
|  +---------------------------+  |   |                                  |
|  |event loop()               |  |   |  call do_in_gtk_eventloop(Fn)    |
|  |                           |  |   |    This Fn has access to DATA    |
|  |  +----------------------+ |  |   |                                  |
|  |  |closure added with    | |  |   |                                  |
|  |  |idle_add() to execute | |  |   +----------------------------------+
|  |  |on the gtk thread {   | |  |
|  |  |                      <-----------------------------------------------+
|  |  |  Pop (Fn,Cb) from RX | |  |                                          |
|  |  |  Call Fn(DATA)       | |  |                                          |
|  |  |  Signal end of Fn    | |  |   +----------------------------------+   |
|  |  |   via Cb             | |  |   |do_in_gtk_eventloop(Fn)           |   |
|  |  |                      | |  |   |                                  |   |
|  |  |                      | |  |   |  Box closure Fn and transmute    |   |
|  |  +----------------------+ |  |   |   livetime to 'static            |   |
|  |                           |  |   |                                  |   |
|  +---------------------------+  |   |  Create a signal Cb              |   |
|                                 |   |  Push (boxed Fn, Cb) to TX       |   |
+---------------------------------+   |  Add this closure via idle_add() +---+
                                      |  Wait for the signal Cb          |
                                      |  return                          |
                                      |                                  |
                                      |                                  |
                                      +----------------------------------+
init_storage()初始化DATA,RX和TX。
使用unsafe
有一个使用unsafe,只是为了方便。它允许闭包引用局部堆栈,而不是要求闭包上的'static。
您可以轻松删除unsafe,但之后您被迫将所有内容移动到with_ref闭包中。
依赖项
~17MB
~401K SLoC