From 190c427b008991737c36f9b4bdf405a91411651f Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Wed, 16 Sep 2020 22:25:39 +0200 Subject: [PATCH 1/2] Better multithreading example --- relm-examples/examples/multithread.rs | 177 +++++++++++++++++--------- 1 file changed, 118 insertions(+), 59 deletions(-) diff --git a/relm-examples/examples/multithread.rs b/relm-examples/examples/multithread.rs index 747c7034..0ea8dbba 100644 --- a/relm-examples/examples/multithread.rs +++ b/relm-examples/examples/multithread.rs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Boucher, Antoni + * Copyright (c) 2017 Boucher, Antoni * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -18,91 +18,150 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +extern crate gtk; +use gtk::{ + Button, ButtonExt, ContainerExt, Inhibit, Label, LabelExt, Orientation::Vertical, WidgetExt, + Window, WindowType, +}; +use relm::{connect, Channel, Relm, Update, Widget, WidgetTest}; +use relm_derive::Msg; use std::thread; use std::time::Duration; -use gtk::{ - Inhibit, - LabelExt, - OrientableExt, - WidgetExt, -}; -use gtk::Orientation::Vertical; -use relm::{Channel, Relm, Widget}; -use relm_derive::{Msg, widget}; +struct Model { + counter: i32, +} -use self::Msg::*; +#[derive(Msg)] +enum Msg { + Decrement, + Increment(i32), + Quit, +} + +// Create the structure that holds the widgets used in the view. +#[derive(Clone)] +struct Widgets { + counter_label: Label, + minus_button: Button, + plus_button: Button, + window: Window, +} -pub struct Model { +struct Win { + model: Model, + widgets: Widgets, _channel: Channel, - text: String, } -#[derive(Clone, Msg)] -pub enum Msg { - Quit, - Value(i32), +impl Update for Win { + // Specify the model used for this widget. + type Model = Model; + // Specify the model parameter used to init the model. + type ModelParam = (); + // Specify the type of the messages sent to the update function. + type Msg = Msg; + + fn model(_: &Relm, _: ()) -> Model { + Model { counter: 0 } + } + + fn update(&mut self, event: Msg) { + let label = &self.widgets.counter_label; + + match event { + Msg::Decrement => { + self.model.counter -= 1; + // Manually update the view. + label.set_text(&self.model.counter.to_string()); + } + Msg::Increment(step) => { + self.model.counter += step; + label.set_text(&self.model.counter.to_string()); + } + Msg::Quit => gtk::main_quit(), + } + } } -#[widget] impl Widget for Win { - fn model(relm: &Relm, _: ()) -> Model { + // Specify the type of the root widget. + type Root = Window; + + // Return the root widget. + fn root(&self) -> Self::Root { + self.widgets.window.clone() + } + + fn view(relm: &Relm, model: Self::Model) -> Self { + // Create the view using the normal GTK+ method calls. + let vbox = gtk::Box::new(Vertical, 0); + + let plus_button = Button::with_label("+"); + vbox.add(&plus_button); + + let counter_label = Label::new(Some("0")); + vbox.add(&counter_label); + + let minus_button = Button::with_label("-"); + vbox.add(&minus_button); + + let window = Window::new(WindowType::Toplevel); + + window.add(&vbox); + + // Send the message Increment when the button is clicked. + connect!(relm, plus_button, connect_clicked(_), Msg::Increment(1)); + connect!(relm, minus_button, connect_clicked(_), Msg::Decrement); + connect!( + relm, + window, + connect_delete_event(_, _), + return (Some(Msg::Quit), Inhibit(false)) + ); + + window.show_all(); + let stream = relm.stream().clone(); // Create a channel to be able to send a message from another thread. - let (channel, sender) = Channel::new(move |num| { + let (channel, sender) = Channel::new(move |step| { // This closure is executed whenever a message is received from the sender. // We send a message to the current widget. - stream.emit(Value(num)); + stream.emit(Msg::Increment(step)); }); thread::spawn(move || { - thread::sleep(Duration::from_millis(200)); - // Send a message from the other thread. - // The value 42 will be received as the num parameter in the above closure. - sender.send(42).expect("send message"); + let mut step = 0; + loop { + thread::sleep(Duration::from_millis(1000)); + // Send a message from the other thread. + // The value 42 will be received as the num parameter in the above closure. + sender.send(step).expect("send message"); + step += 1; + } }); - Model { + + Win { + model, + widgets: Widgets { + counter_label, + minus_button, + plus_button, + window, + }, _channel: channel, - text: "Computing...".to_string(), } } +} - fn update(&mut self, event: Msg) { - match event { - Quit => gtk::main_quit(), - Value(num) => self.model.text = num.to_string(), - } - } +impl WidgetTest for Win { + type Widgets = Widgets; - view! { - gtk::Window { - gtk::Box { - orientation: Vertical, - gtk::Label { - text: &self.model.text, - }, - }, - delete_event(_, _) => (Quit, Inhibit(false)), - } + fn get_widgets(&self) -> Self::Widgets { + self.widgets.clone() } } fn main() { Win::run(()).expect("Win::run failed"); } - -#[cfg(test)] -mod tests { - use relm_test::{relm_observer_new, relm_observer_wait}; - - use crate::Msg::Value; - use crate::Win; - - #[test] - fn channel() { - let (component, _widgets) = relm::init_test::(()).expect("init_test failed"); - let observer = relm_observer_new!(component, Value(_)); - relm_observer_wait!(let Value(value) = observer); - assert_eq!(value, 42); - } -} From 6cb193e0f607f936b4de2dfa4387a0001e76a31b Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Wed, 16 Sep 2020 22:32:45 +0200 Subject: [PATCH 2/2] Better multi-threading example --- relm-examples/examples/multithread.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/relm-examples/examples/multithread.rs b/relm-examples/examples/multithread.rs index 0ea8dbba..ab0b84be 100644 --- a/relm-examples/examples/multithread.rs +++ b/relm-examples/examples/multithread.rs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Boucher, Antoni + * Copyright (c) 2018 Boucher, Antoni * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -49,6 +49,7 @@ struct Widgets { window: Window, } +// The channel is saved so it does not get droped struct Win { model: Model, widgets: Widgets, @@ -133,9 +134,10 @@ impl Widget for Win { thread::spawn(move || { let mut step = 0; loop { + // The thread sleeps for one second and then sends a message to increment the counter thread::sleep(Duration::from_millis(1000)); // Send a message from the other thread. - // The value 42 will be received as the num parameter in the above closure. + // The value of step will be received as the num parameter in the above closure. sender.send(step).expect("send message"); step += 1; }