在Rust中处理GTK +事件的另一种方法 [英] Alternative way to handle GTK+ events in Rust

查看:238
本文介绍了在Rust中处理GTK +事件的另一种方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前,我使用 Rc RefCell 管理GTK +事件,如以下示例所示:

Currently, I manage GTK+ events with Rc and RefCell as shown in the following example:

extern crate gtk;

use std::cell::RefCell;
use std::rc::Rc;

use gtk::{Button, ButtonExt, ContainerExt, Inhibit, Label, WidgetExt, Window, WindowType};
use gtk::Orientation::Vertical;

struct Model {
    count: i32,
}

fn main() {
    gtk::init().unwrap();

    let window = Window::new(WindowType::Toplevel);

    let model = Rc::new(RefCell::new(Model { count: 0 }));

    let vbox = gtk::Box::new(Vertical, 0);
    window.add(&vbox);

    let label = Label::new(Some("0"));
    vbox.add(&label);

    let button = Button::new_with_label("Increment");
    vbox.add(&button);
    window.show_all();

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    {
        let model = model.clone();
        button.connect_clicked(move |_| {
            {
                (*model.borrow_mut()).count += 1;
            }
            label.set_text(&format!("{}", (*model.borrow()).count));
        });
    }

    gtk::main();
}

此代码的主要问题是由于 RefCell s而需要的样板.

My main issue with this code is the boilerplate needed because of the RefCells.

我还觉得这是一种不好的做法,并且可能导致恐慌(这不是我的主要问题,所以不要建议使用 Mutex ,因为在某些示例中,这可能导致死锁).

Also I feel like it is bad practise and can lead to panics (this is not my main issue so don't propose to use a Mutex because in some examples, that can lead to a deadlock).

所以我想我可以用类似于Elm的方式处理事件:一个函数接收可以更新模型的信号.但是,我无法在Rust中实现这一点.这是一个尝试:

So I thought I could handle events in a way similar to Elm: with one function receiving signals where the model could be updated. However, I am unable to implement this in Rust. Here is an attempt:

extern crate gtk;

use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;

use gtk::{Button, ButtonExt, ContainerExt, Inhibit, Label, WidgetExt, WindowType};
use gtk::Orientation::Vertical;

use Message::Increment;

enum Message {
    Increment,
}

struct Window {
    label: Label,
    model: Model,
    queue: Rc<RefCell<VecDeque<Message>>>,
    view: gtk::Window,
}

impl Window {
    fn new() -> Self {
        let window = gtk::Window::new(WindowType::Toplevel);

        let vbox = gtk::Box::new(Vertical, 0);
        window.add(&vbox);

        let label = Label::new(Some("0"));
        vbox.add(&label);

        let button = Button::new_with_label("Increment");
        vbox.add(&button);
        window.show_all();

        window.connect_delete_event(|_, _| {
            gtk::main_quit();
            Inhibit(false)
        });

        let queue = Rc::new(RefCell::new(VecDeque::new()));

        {
            let queue = queue.clone();
            button.connect_clicked(move |_| {
                (*queue.borrow_mut()).push_back(Increment);
            });
        }

        Window {
            label: label,
            queue: queue,
            model: Model { count: 0 },
            view: window,
        }
    }

    // How to call this method when a message is received?
    fn update(&mut self, message: Message) {
        match message {
            Increment => {
                self.model.count += 1;
                self.label.set_text(&format!("{}", self.model.count));
            },
        }
    }
}

struct Model {
    count: i32,
}

fn main() {
    gtk::init().unwrap();

    let window = Window::new();

    gtk::main();
}

消息 queue 更新时,如何调用 update()方法?

How can I call the update() method when the message queue is updated?

这是可行的方法吗?

如果没有,您知道有什么替代方法可以解决此问题吗?

If not, do you know any alternatives that would provide a solution to this issue?

也许可以使用基于 future 板条箱的解决方案?在这种情况下,我该如何管理两个主循环(gtk +一个和 tokio 一个).

Perhaps some solution based on the future crate could be used? In this case, how do I manage both main loops (the gtk+ one and the tokio one).

还是使用渠道的解决方案?

Or a solution using channels?

推荐答案

我找到了解决此问题的方法此处.

I found a solution to this issue here.

这是我通过示例获得的成就:

Here is what I achieved with my example:

extern crate gtk;

use gtk::{Button, ButtonExt, ContainerExt, Inhibit, Label, WidgetExt, WindowType};
use gtk::Orientation::Vertical;

use Message::Increment;

macro_rules! connect {
    ($source:ident :: $event:ident, $target:ident :: $message:expr) => {
        let target = &mut *$target as *mut _;
        $source.$event(move |_| {
            let target: &mut Window = unsafe { &mut *target };
            target.update($message);
        });
    };
}

enum Message {
    Increment,
}

struct Window {
    label: Label,
    model: Model,
    view: gtk::Window,
}

impl Window {
    fn new() -> Box<Self> {
        let window = gtk::Window::new(WindowType::Toplevel);

        let vbox = gtk::Box::new(Vertical, 0);
        window.add(&vbox);

        let label = Label::new(Some("0"));
        vbox.add(&label);

        let button = Button::new_with_label("Increment");
        vbox.add(&button);
        window.show_all();

        window.connect_delete_event(|_, _| {
            gtk::main_quit();
            Inhibit(false)
        });

        let mut window = Box::new(Window {
            label: label,
            model: Model { count: 0 },
            view: window,
        });

        connect!(button::connect_clicked, window::Increment);

        window
    }

    fn update(&mut self, message: Message) {
        match message {
            Increment => {
                self.model.count += 1;
                self.label.set_text(&format!("{}", self.model.count));
            },
        }
    }
}

struct Model {
    count: i32,
}

fn main() {
    gtk::init().unwrap();

    let _window = Window::new();

    gtk::main();
}

您认为这种对 unsafe 的使用会导致段错误吗?例如,如果 button 可以超过 window ,那么 window 可以在释放后使用,不是吗?有没有办法为此建立一个安全的包装器?

Do you think this use of unsafe can cause segfault? For instance, if the button could outlive the window, the window could be used after being freed, no? Is there a way to build a safe wrapper around this?

从美学的角度来看,有一种方法可以实现这一目标:

On an esthetical note, is there a way to achieve this:

connect!(button::clicked, window::Increment);

这篇关于在Rust中处理GTK +事件的另一种方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆