Rust:从 DrawingArea::connect_draw 中的频道接收?
Rust: Receive from channel in DrawingArea::connect_draw?
我正在尝试制作一个程序,其中 window 显示模拟的变化状态。我对如何做到这一点的想法(我对其他建议非常开放)是产生一个处理模拟逻辑的线程,并发送详细说明绘制处理程序中读取的状态的消息。我目前对此的尝试如下:
extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
fn main(){
let (tx, rx )= mpsc::channel();
let app = Application::builder()
.application_id("org.example.HelloWorld")
.build();
app.connect_activate(|app| {
let draw_area = DrawingArea::new();
let _id = draw_area.connect_draw(|_unused, f| {
let red : f64 = rx.recv().unwrap();
f.set_source_rgb(red, 0.0, 0.0);
f.paint().expect("Painting failed");
Inhibit(false)
});
let win = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Hello, World!")
.build();
win.add(&draw_area);
win.connect_event(|w, _g|{ //Placeholder until I learn to make queue_draw fire on a timer
w.queue_draw();
Inhibit(false)
});
win.show_all();
});
thread::spawn(move || {
for _i in 0..9 {
thread::sleep(time::Duration::from_millis(1000));
tx.send(rand::random::<f64>()).unwrap();
}
});
app.run();
}
这失败了,因为 rx
活得不够长。我想我明白编译器为什么这么想,但我不清楚如何解决这个问题,或者这是否是一个可行的方法。
作为一个额外的问题,这段代码中有一个绝对的黑客攻击 win.connect_event
调用,它让我可以通过任何操作触发重绘。我希望它只是定期触发,但我还没有找到这样做的方法 - 任何帮助将不胜感激。
我最终想出了一个办法(问 )。描述我学到的东西:
rx
必须 moved
到使用它的闭包中,如果闭包可能比创建 rx
的原始范围长。 (这在问题的代码中发生了两次)。
- 此代码有两个嵌套闭包,
rx
在每个闭包之外创建。你可以“给” rx
给外层闭包,但它不能再给内层闭包,因为外层闭包可能会被重复调用(因为所有 rust 都知道)。如果它放弃了它唯一的副本,它可能没有一个用于第二次调用,这是不安全的。
我的解决方案是在 connect_activate
闭包之外定义 connect_draw
外壳。请注意,此解决方案仍然不完美,因为我还没有弄清楚如何像成年人一样在计时器上触发 connect_draw
,但频道问题似乎已解决:
extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
extern crate glib;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
fn main(){
let app = Application::builder()
.application_id("org.example.HelloWorld")
.build();
let (tx, rx ) : (Sender<f64>, Receiver<f64>)= mpsc::channel();
gtk::init().expect("GTK init failed");
let draw_area = DrawingArea::new();
let _id = draw_area.connect_draw(move |_unused, f| {
let red = rx.recv().unwrap();
f.set_source_rgb(red,0.5, 0.5);
f.paint().expect("Painting failed");
Inhibit(false)
});
app.connect_activate(move |app| {
let win = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Hello, World!")
.build();
win.add(&draw_area);
win.show_all();
win.connect_event(|w, _g|{ //HORRIBLE HACK HELP FIX
w.queue_draw();
Inhibit(false)
});
});
thread::spawn(move || {
loop {
thread::sleep(time::Duration::from_millis(100));
tx.send(rand::random::<f64>()).unwrap();
}
});
app.run();
}
我正在尝试制作一个程序,其中 window 显示模拟的变化状态。我对如何做到这一点的想法(我对其他建议非常开放)是产生一个处理模拟逻辑的线程,并发送详细说明绘制处理程序中读取的状态的消息。我目前对此的尝试如下:
extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
fn main(){
let (tx, rx )= mpsc::channel();
let app = Application::builder()
.application_id("org.example.HelloWorld")
.build();
app.connect_activate(|app| {
let draw_area = DrawingArea::new();
let _id = draw_area.connect_draw(|_unused, f| {
let red : f64 = rx.recv().unwrap();
f.set_source_rgb(red, 0.0, 0.0);
f.paint().expect("Painting failed");
Inhibit(false)
});
let win = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Hello, World!")
.build();
win.add(&draw_area);
win.connect_event(|w, _g|{ //Placeholder until I learn to make queue_draw fire on a timer
w.queue_draw();
Inhibit(false)
});
win.show_all();
});
thread::spawn(move || {
for _i in 0..9 {
thread::sleep(time::Duration::from_millis(1000));
tx.send(rand::random::<f64>()).unwrap();
}
});
app.run();
}
这失败了,因为 rx
活得不够长。我想我明白编译器为什么这么想,但我不清楚如何解决这个问题,或者这是否是一个可行的方法。
作为一个额外的问题,这段代码中有一个绝对的黑客攻击 win.connect_event
调用,它让我可以通过任何操作触发重绘。我希望它只是定期触发,但我还没有找到这样做的方法 - 任何帮助将不胜感激。
我最终想出了一个办法(问
rx
必须moved
到使用它的闭包中,如果闭包可能比创建rx
的原始范围长。 (这在问题的代码中发生了两次)。- 此代码有两个嵌套闭包,
rx
在每个闭包之外创建。你可以“给”rx
给外层闭包,但它不能再给内层闭包,因为外层闭包可能会被重复调用(因为所有 rust 都知道)。如果它放弃了它唯一的副本,它可能没有一个用于第二次调用,这是不安全的。
我的解决方案是在 connect_activate
闭包之外定义 connect_draw
外壳。请注意,此解决方案仍然不完美,因为我还没有弄清楚如何像成年人一样在计时器上触发 connect_draw
,但频道问题似乎已解决:
extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
extern crate glib;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
fn main(){
let app = Application::builder()
.application_id("org.example.HelloWorld")
.build();
let (tx, rx ) : (Sender<f64>, Receiver<f64>)= mpsc::channel();
gtk::init().expect("GTK init failed");
let draw_area = DrawingArea::new();
let _id = draw_area.connect_draw(move |_unused, f| {
let red = rx.recv().unwrap();
f.set_source_rgb(red,0.5, 0.5);
f.paint().expect("Painting failed");
Inhibit(false)
});
app.connect_activate(move |app| {
let win = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Hello, World!")
.build();
win.add(&draw_area);
win.show_all();
win.connect_event(|w, _g|{ //HORRIBLE HACK HELP FIX
w.queue_draw();
Inhibit(false)
});
});
thread::spawn(move || {
loop {
thread::sleep(time::Duration::from_millis(100));
tx.send(rand::random::<f64>()).unwrap();
}
});
app.run();
}