在 Rust 中处理 GTK+ 事件的替代方法

2023-12-13

目前,我管理 GTK+ 活动Rc and RefCell如下例所示:

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();
}

我对这段代码的主要问题是所需的样板,因为RefCells.

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

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

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();
}

我怎样才能打电话给update()消息时的方法queue更新了吗?

这是一个可行的方法吗?

如果没有,您是否知道可以解决此问题的任何替代方案?

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

或者使用渠道的解决方案?


我找到了这个问题的解决方案here.

这是我通过我的例子取得的成果:

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, the window释放后可以使用吧? 有没有办法围绕这个构建一个安全的包装器?

从美学角度来看,有没有办法实现这一点:

connect!(button::clicked, window::Increment);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Rust 中处理 GTK+ 事件的替代方法 的相关文章

随机推荐

  • Google Script CacheService 在 sendMail 中为 inlineImages 缓存图像

    我想将图像存储在 Google Script 的缓存服务中 然后将此图像作为内联图像插入 HTML 邮件中 我已经尝试让它发挥作用 但到目前为止还没有成功 记录器中以下代码的错误是 无效参数 附件 如果我检查它显示 sendMail 中的
  • 如何动态新的匿名类?

    在 C 3 0 中 您可以使用以下语法创建匿名类 var o1 new Id 1 Name Foo 有没有办法动态创建这些匿名类到变量 Example var o1 new Id 1 Name Foo var o2 new SQ 2 Bir
  • 在一个查询 MYSQL 中插入和更新

    我有一个简单的 INSERT 查询 在单击按钮的事件上运行 这会获取变量并将它们插入到order table 我也想更新我的bands表将库存减少 1 直到达到 0 然后显示空或已售完 而不是变为负数 例如 6 要知道选择了什么频段 我可以
  • Android逐字符显示文字动画

    任何人都知道执行动画的任何有效方法 所要做的就是逐个字符地显示文本 喜欢 T Th Thi This This i This is 等等 Thanks 这可能不是最优雅的解决方案 但最简单的可能是一个快速子类TextView with a
  • XSL 在 CDATA 中取消转义 HTML

    我正在尝试转换 XML
  • WebDriver 模拟桌面浏览器中的触摸事件

    我正在尝试使用 Chrome 和 Firefox 浏览器在基于 Web 的应用程序中模拟一些触摸事件 例如交换 点击 我尝试用以下方法模拟触摸事件Actions Hammer js Yahoo lib YUI 但它们都不适合我 在桌面浏览器
  • Django:CSV模型导入[重复]

    这个问题在这里已经有答案了 我需要一种将数据简历导入数据库的方法 csv 在字段方面与我的模型之一匹配 在 Django 中 有没有推荐的方法 包来做这样的事情 我查看了此处的 django csvimport http pypi pyth
  • 使用 jQuery select2 设置多个值[重复]

    这个问题在这里已经有答案了 我使用 jQuery select2 选择多个列表项 但不幸的是 下面的代码必须将所有 3 个值加载到选择字段中 但是它只加载第一个项目 如何将所有 3 个项目加载到 select2 列表中
  • 防止一段代码在协程中同时执行

    我需要保护一段代码免于在协程 中同时执行 防止多线程环境中的并发执行很简单 只需使用std lock guard类模板 然而 我的协程是从单个线程调用的 因此该解决方案不适用 以下是我想要完成的 伪 代码 future
  • Phoenix 夹具 json 文件

    如何在我的 phoenix 项目中加载 JSON 夹具文件 当我尝试像 Application app dir my app priv 这样的东西时 它给了我一个编译路径 我不能在我的测试中使用它 还有其他方法可以从 test suppor
  • 当我关闭 Pygame 时屏幕冻结

    该代码加载了一个 pygame 屏幕窗口 但是当我单击 X 关闭它时 它变得没有响应 我在 64 位系统上运行 使用 32 位 python 和 32 位 pygame from livewires import games color g
  • 不带 GROUP BY 子句的 MySQL 聚合函数

    在 MySQL 中 我观察到尽管没有 GROUP BY 子句 但在 SELECT 列表中使用 AGGREGATE FUNCTION 的语句仍会被执行 如果我们这样做 其他 RDBMS 产品 例如 SQL Server 会抛出错误 例如 SE
  • 关于 git pull 的合并/变基步骤的困惑

    来自 Loeliger 2ed 的 Version Control with Git 关于 git pull 中的合并或变基步骤 在拉取操作的第二步中 Git 执行 合并 默认 或变基操作 关于 git pull 中的合并步骤 在此示例中
  • 对出现错误的行使用带有重复标识符的扩展

    我的数据如下所示 df lt read table header T text GeneID Gene Name Species Paralogues Domains Functional Diversity 1234 DDR1 hsapi
  • 如何在android中动态启动和结束进度条

    当我从第一个活动类跳过第二个活动类时 我将开始对第二个活动中的某些图像进行图像处理 然后直到新图像出现在屏幕上时 我不想启动进度栏 然后在新图像出现在屏幕上时完成 我怎样才能做到这一点 使用 ProgreaaDialog 和 AsyncTa
  • 调用方法 Location.toString 的权限被拒绝

    我用的是YUI 有时我会看到 调用方法 Location toString 的权限被拒绝 错误 它在connection js中报告 我还没有找到任何线索为什么会出现这个错误 很奇怪 如果您通过跨域 iframe 处理任何 Flash 那么
  • 如何在 Pyspark 中注册不带参数的 UDF

    我已经尝试使用 lambda 函数带参数的 Spark UDF 并注册它 但是我怎样才能创建没有参数和注册器的 udf 我已经尝试过这个我的示例代码将期望显示当前时间 从日期时间导入日期时间 从 pyspark sql functions
  • 如何删除等号('=')之前的所有字符串文本Java

    我想解析一个字符串 以便构建一个 XML 文档 I have String value path Some Xpath Here 我是这样解析的 private void parseXpath String s path Some Xpat
  • JVM 如何确保 System.identityHashCode() 永远不会改变?

    通常默认实现Object hashCode 是内存中对象分配地址的某个函数 尽管这不是由JLS 鉴于虚拟机在内存中分流对象 为什么返回的值System identityHashCode 在对象的生命周期中永远不会改变 如果是 一次性 计算
  • 在 Rust 中处理 GTK+ 事件的替代方法

    目前 我管理 GTK 活动Rc and RefCell如下例所示 extern crate gtk use std cell RefCell use std rc Rc use gtk Button ButtonExt ContainerE