Multithreading gtk rs:如何从另一个线程更新视图
我正在使用创建一个UI应用程序。在该应用程序中,我必须生成一个线程来与另一个进程持续通信。有时,我必须根据线程中发生的情况更新UI。但是,我不知道该怎么做,因为我无法跨线程保存对UI任何部分的引用 以下是我尝试的代码:Multithreading gtk rs:如何从另一个线程更新视图,multithreading,rust,gtk3,gtk-rs,Multithreading,Rust,Gtk3,Gtk Rs,我正在使用创建一个UI应用程序。在该应用程序中,我必须生成一个线程来与另一个进程持续通信。有时,我必须根据线程中发生的情况更新UI。但是,我不知道该怎么做,因为我无法跨线程保存对UI任何部分的引用 以下是我尝试的代码: 使用gtk; fn main(){ 让申请= gtk::Application::new(一些(“com.github.gtk-rs.examples.basic”),Default::Default()).unwrap() 应用程序。连接并激活(|应用程序|{ 让ui\u mo
使用gtk;
fn main(){
让申请=
gtk::Application::new(一些(“com.github.gtk-rs.examples.basic”),Default::Default()).unwrap()
应用程序。连接并激活(|应用程序|{
让ui\u model=build\u ui(app);
设置(ui_模型);
});
运行(&[]);
}
结构UiModel{main_buffer:gtk::TextBuffer}
fn构建ui(应用程序:>k::应用程序)->UiModel{
让glade_src=包含_str!(“test.glade”);
让builder=gtk::builder::new();
建设者
.add_from_string(glade_src)
.expect(“无法从字符串添加”);
让窗口:gtk::ApplicationWindow=builder.get_对象(“窗口”).unwrap();
设置应用程序(一些(应用程序));
window.show_all();
让main_text_视图:gtk::TextView=builder.get_对象(“main_text_视图”)
返回模型{
主缓冲区:主文本视图。获取缓冲区()。展开(),
};
}
fn设置(ui:UiModel){
让子进程=命令::新建(“sh”)
.args(&[“-c”,“while true;do date;sleep 2;done”])
.stdout(Stdio::piped())
.spawn()
.unwrap();
let incoming=child_process.stdout.unwrap();
std::thread::spawn(move |{/好的,我解决了这个问题。对于未来的任何人,这里是解决方案
glib::idle_add(| |{})
允许您从UI线程(thansk@Zan Lynx)上的另一个线程运行闭包。这足以解决线程安全问题,但不足以绕过借用检查器。没有GTKObject
可以在线程之间安全发送,因此另一个线程甚至不能保存对它的引用,即使它永远不会使用它。因此,您需要在UI线程上全局存储UI引用并设置通信线程之间的通道。以下是我一步一步做的:
std::sync::mpsc
,但从长远来看,另一种方法可能更好外部板条箱gio;
外部板条箱gtk;
外部板条箱盘古;
使用gio::前奏::*;
使用gtk::前奏::*;
使用std::cell::RefCell;
使用std::io::{BufRead,BufReader};
使用std::process::{Command,Stdio};
使用std::sync::mpsc;
fn main(){
让申请=
gtk::Application::new(一些(“com.github.gtk-rs.examples.basic”),Default::Default()
.unwrap();
应用程序。连接并激活(|应用程序|{
让ui\u model=build\u ui(app);
设置(ui_模型);
});
运行(&[]);
}
结构模型{
主缓冲区:gtk::TextBuffer,
}
fn构建ui(应用程序:>k::应用程序)->UiModel{
让glade_src=包含_str!(“test.glade”);
让builder=gtk::builder::new();
建设者
.add_from_string(glade_src)
.expect(“无法从字符串添加”);
让窗口:gtk::ApplicationWindow=builder.get_对象(“窗口”).unwrap();
设置应用程序(一些(应用程序));
window.show_all();
让main_text_视图:gtk::TextView=builder.get_对象(“main_text_视图”).unwrap();
返回模型{
主缓冲区:主文本视图。获取缓冲区()。展开(),
};
}
fn设置(ui:UiModel){
let(tx,rx)=mpsc::channel();
全局。带有(|全局|{
*global.borrow_mut()=Some((ui,rx));
});
让子进程=命令::新建(“sh”)
.args(&[“-c”,“while true;do date;sleep 2;done”])
.stdout(Stdio::piped())
.spawn()
.unwrap();
let incoming=child_process.stdout.unwrap();
std::thread::spawn(移动| |){
&BufReader::新的(传入的).lines()。用于每个(|行|{
让data=line.unwrap();
//通过信道发送数据
发送(数据).unwrap();
//然后告诉UI线程从该通道读取
glib::source::idle|u add(| |{
检查是否有新消息();
返回glib::source::Continue(false);
});
});
});
}
//用于存储ui和输入通道的全局变量
//仅在主线程上
本地线程(
静态全局:RefCell=RefCell::new(无);
);
//函数检查新消息是否已通过
//全局接收器,如果是,则将其添加到UI。
fn检查是否有新消息(){
全局。带有(|全局|{
如果让一些((ui,rx))=&*global.borrow(){
let received:String=rx.recv().unwrap();
ui.main\u buffer.set\u text(&received);
}
});
}
我在Rust中没有这样做,因此一些常规的建议是:如果你有一个线程运行速度快且数据量大,那么你可能无法轻松为GUI创建数据快照。因此,请确保你可以将其分块锁定,这样工作线程就不会经常被阻塞。然后,我会做你在GUI中需要做的事情,只读取项目的数据显示在屏幕上。不要创建所有数据的列表或表格视图。创建一个带有滚动条的视图,滚动条显示模糊的图像
| std::thread::spawn(move || {
| _____^^^^^^^^^^^^^^^^^^_-
| | |
| | `*mut *mut gtk_sys::_GtkTextBufferPrivate` cannot be sent between threads safely