I'm sure there's an easy way to do this but I don't know what it is. I have a very basic gtk::{Application, ApplicationWindow, DrawingArea};
setup. I want the DrawingArea::connect_draw
closure to be triggered repeatedly on a timer, so it updates according to some changing state. (It would also be cool if it could be actively triggered by other threads, but a timer is fine.)
So far everything I've found that would work on a timer fails because it would mean moving the ApplicationWindow
to another thread. (fails with NonNull<GObject> cannot be shared between threads safely
) What I have currently triggers redraw on generic events, so if I click my mouse on the window it will redraw, but not do so automatically.
That code is below, but please show me how to make this work?
//BOILER PLATE SCROLL DOWN
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();
//IMPORTANT STUFF STARTS HERE
win.connect_event(|w, _g|{ //HORRIBLE HACK HELP FIX
w.queue_draw();
Inhibit(false)
});
glib::timeout_add_seconds(1, ||{
println!("I wish I could redraw instead of printing this line");
Continue(true)
});
//fails with "`NonNull<GObject>` cannot be shared between threads safely" :
// glib::timeout_add_seconds(1, ||{
// win.queue_draw();
// Continue(true)
// });
//IMPORTANT STUFF ENDS HERE
});
thread::spawn(move || {
loop {
thread::sleep(time::Duration::from_millis(100));
tx.send(rand::random::<f64>()).unwrap();
}
});
app.run();
}
EDIT: I tried a mutex version, maybe have implemented it wrong. The following code gives the same error (NonNull<GObject> cannot be shared between threads safely
)
let mut_win = Mutex::new(win);
let arc_win = Arc::new(mut_win);
glib::timeout_add_seconds(1, move ||{
let mut w = arc_win.lock().unwrap();
(*w).queue_draw();
Continue(true)
});