#gtk #thread #widgets #modifying

gtk-rs-state

允许从其他线程修改gtk-rs小部件

3个不稳定版本

0.4.1 2020年12月12日
0.4.0 2020年11月22日
0.3.0 2020年1月25日

GUI中排名#369

每月下载量34
flowide中使用

MIT许可证

21KB
153

Build Status

gtk-rs-state

reddit问题

在稳定版上运行。

这不是最快的实现,但对于几乎所有用例来说应该足够了。

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<&gtk::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(&gtk::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