2 个版本
使用旧的 Rust 2015
0.2.1 | 2018 年 11 月 17 日 |
---|---|
0.2.0 | 2018 年 11 月 17 日 |
在 GUI 中排名 494
26KB
155 行
从其他线程修改 gtk 小部件
在稳定版本上运行。
这不是最快的实现,但对于几乎所有用例来说,这应该足够了。
Cargo.toml
你可以使用最新版本,但需要在 gtk 旁边安装 glib。
[dependencies]
gtk-fnonce-on-eventloop = "0.2"
此 crate 使用以下依赖项
- 版本 = "0.5.0" 且具有功能 = ["v3_10"] 的 gtk
- 版本 = "0.6.0" 的 glib
示例用法
使用此方法归结为
- 调用 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-fnonce-on-eventloop 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>) );
}
它是如何工作的?
- 您提供给
do_in_gtk_eventloop(闭包)
的闭包将通过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
闭包中。
依赖项
~13MB
~314K SLoC