Rust GUI:简介,也就是 Rust GUI 库的现状(截至 2021 年 1 月)Rust GUI 的现状如何?自述文件 工作 完全不工作! 其他问题 结论

2025-05-25

Rust GUI:简介,也就是 Rust GUI 库的现状(截至 2021 年 1 月)

Reddit 徽标 Rust GUI 的现状如何?

自述文件

在职的

根本不起作用!

其他问题

结论

[图片由Kelly SikkemaUnsplash上拍摄,已修改(裁剪)]

本文是一篇讨论的后续文章。请分享你的经验,我很乐意听听!

介绍

几个月前,我调查了一下 Rust 的 GUI 现状。说实话,调查结果很糟糕。

从互联网上进行一些搜索开始,除了 Reddit 和其他网站上的一些问题之外,您还会得到一些(非常旧的)文章。

Reddit 徽标 Rust GUI 的现状如何?

自从几个月前得知 Rust 的存在以来,我一直对它非常感兴趣。然而,当时让我望而却步的一件事是它缺乏跨平台设备的(结构化) GUI 绑定。但几个月后,有没有一个好的 GUI 库可以用来开发“复杂”的应用程序?或者……

又老又陈旧。

最终,您获得的唯一指南(供所有人参考)是Are We GUI Yet网站,但它根本没有帮助。

GitHub 徽标 areweguiyet / areweguiyet

为 Rust 社区建立的网站

自述文件

想找 Rust 的 GUI 库吗?那你来对地方了!

地位

本网站已尽力维护。下一步计划可能将其替换为类似lib.rs 的自动化系统。

如果此 repo 中的 crate 配置未覆盖 CI,则 CI 每周运行一次以更新来自 crates.io 的 crate 信息。

这是什么?

arewegameyet 、 arewewebyetarewelearningyet的配套网站

贡献

让人们轻松(嘿,那就是你! 😊 ) 贡献,AreWeGuiYet 使用自定义的 Rust CLI。它目前仍在开发中,边缘略显粗糙。

工作流程:

  • 叉 AreWeGuiYet
  • 克隆你的 fork
  • cd到 CLI 目录(cli)(它目前使用相对路径 😬 )
  • 构建并运行 CLI
    • 用法:cargo run -- [--clean] <command> [flags]
    • 帮助:cargo run -- help
  • 完成后...

AreWeGUIYet似乎自 2019 年以来就没有更新过,而且它的界面对搜索合适的库毫无帮助。它唯一的标签是关于 crate 的实现类型。文章不多,教程也不实用,也无法了解那里展示的 crate 的状态。

一些特色板条箱看起来相当粗糙,一些则更先进......但除了尝试每一个之外,没有其他办法可以知道。


天哪。

我确实安装了一些,但结果却是一场灾难,因为至少我尝试过的那些都很糟糕。

后来,暑假期间,我读到了一篇由 boringcactus 撰写的文章。他们的经历与我大致相同,但我意识到,我实际上比他们做得更多……

时间快进到十一月初。我又开始用老办法尝试了。我认真地计划着把清单上的每一个箱子都试一遍。

这次我运气更好。至少我不仅成功开发了一些库,还获得了一些素材来编写教程来帮助其他人。

在这里我会让您胃口大开:接下来还有更多!

免责声明

  • 我仅在我的 Linux 笔记本电脑上尝试了以下操作:抱歉,我没有 Windows 或 Mac!
  • 我通常使用cargo addcargo edit(来安装它:) cargo install cargo-edit,但你可以直接将依赖项添加到Cargo.toml
  • 我尽一切可能让这些 crate 正常工作,而且我是认真的。我甚至在本地打了补丁,提交了一些问题,甚至还提交了 PR。我真心实意地做好了功课。

摘要表

以下是我尝试过的板条箱的摘要,以及我发现它们的状态:

地位 姓名 笔记
在职的 德鲁伊 审查 它依赖于piet 2D 图形库;在 Linux 上,这意味着 GTK/Cairo 2D 原语
弗莱特克 审查 Rust 的 FLTK
GTK+ 审查 官方 GTK+ Rust 支持
冰镇 审查 榆树启发
宫腔内人工授精 审查 基于libui
OrbTK 审查 即使它是为 Redox 设计的,它仍然可以在其他系统上运行
QMetaObject 审查 基于 QT:它严重依赖于 QML
雷姆 审查 受 Elm 启发,GTK 改进
斯凯特 审查 绑定到 Sciter,一个商业库
金牛座 审查 这完全是另一种野兽:它将 Web 应用程序转换为桌面应用程序,同时增加了与 Rust 交互的可能性
虚拟GTK 审查 基于 GTK 构建,并受到 Elm 启发
其他/无法尝试 连杆 审查 这些示例已经运行了。不过,如何构建 GUI 仍然是个谜。
ImGui 审查 与Conrod相同,除了示例之外,没有其他方法可以理解其工作原理
核心基础 审查 无法尝试:我没有 Mac
LittlevGL 审查 无法尝试:它在嵌入式系统上运行
卡斯 审查 无法尝试:我没有 Vulkan 硬件,而且 GTK 后端根本无法工作
不工作 阿苏尔 审查 基于 WebRender。我试了很多次都没成功……结果就是不行!
QT_Widgets 审查 有一个小问题,但是不解决它就无法正常工作

还有两个条目我完全排除了,因为我认为它们不属于 Rust GUI:WebRenderRust-QT-Binding-Generator。Tauri也差点被踢出局,但它保留了下来,因为它完全是另一个野兽”(因为它是技术之间的桥梁,而不是像Rust-QT-Binding-Generator那样是语言之间的桥梁)。


在职的

德鲁伊

我第一次尝试的时候没能用上其中一个 crate(我不记得为什么),但现在Druid已经完美运行了。
它依赖于piet,一个 2D 图形库。在 Linux 上,它封装了GTK3及其cairo 2D 基元,所以请检查你的发行版是否满足所有要求
它还有一个基于 Web 的渲染引擎,并使用wasmwasm-pack

我们来尝试一下吧。

cargo new rust-ui-druid
cd rust-ui-druid
cargo add druid
Enter fullscreen mode Exit fullscreen mode

然后我们可以在src/main.rs中设置我们的第一个应用程序

use druid::widget::{Button, Flex, Label};
use druid::{AppLauncher, LocalizedString, PlatformError, Widget, WidgetExt, WindowDesc, Data};

#[derive(Clone, Data)]
struct Counter(i32);

fn main() -> Result<(), PlatformError> {
    // Window builder. We set title and size
    let main_window = WindowDesc::new(ui_builder)
        .title("Hello, Druid!")
        .window_size((200.0, 100.0));

    // Data to be used in the app (=state)
    let data: Counter = Counter(0);

    // Run the app
    AppLauncher::with_window(main_window)
        .use_simple_logger() // Neat!
        .launch(data)
}

fn ui_builder() -> impl Widget<Counter> {
    // The label text will be computed dynamically based on the current locale and count
    let text = LocalizedString::new("hello-counter")
        .with_arg("count", |data: &Counter, _env| (*data).0.into());
    let label = Label::new(text).padding(5.0).center();

    // Two buttons with on_click callback
    let button_plus = Button::new("+1")
        .on_click(|_ctx, data: &mut Counter, _env| (*data).0 += 1)
        .padding(5.0);
    let button_minus = Button::new("-1")
        .on_click(|_ctx, data: &mut Counter, _env| (*data).0 -= 1)
        .padding(5.0);

    // Container for the two buttons
    let flex = Flex::row()
        .with_child(button_plus)
        .with_spacer(1.0)
        .with_child(button_minus);

    // Container for the whole UI
    Flex::column()
        .with_child(label)
        .with_child(flex)
}
Enter fullscreen mode Exit fullscreen mode

Druid 非常注重数据驱动,因此您会发现 Widget 实现了数据类型:它们本身并不是一个类型。这解释了为什么 Widget 类型必须声明为Widget<T>。我们还注意到 Druid 大量使用了构造函数模式。

代码说明:

#[derive(Clone, Data)]
struct Counter(i32);
Enter fullscreen mode Exit fullscreen mode

我们创建一个自定义数据结构作为应用的状态。它必须派生druid::Data

fn main()必须返回一个Result<(), PlatformError>,因为AppLauncher可能会失败。

在里面,我们使用函数创建一个小部件来创建main()一个新的主窗口描述符。我们设置主窗口的标题和状态。WindowDescui_builder()

我们初始化自定义状态(Data用德鲁伊的术语来说),并将窗口和状态传递给AppLauncher,它将为我们管理应用程序的周期。

请注意,Druid 实现了一个简单的记录器stderr,我认为这对于调试来说是一个简洁的功能。

在 中,fn ui_builder()我们首先创建一个本地化字符串 ( LocalizedString),用于渲染可翻译的文本。此外,这是一个很好的函数,可以将本地化嵌入到常规开发流程中。我们"hello-counter"为其分配键;由于这是默认捆绑的(例如,为了示例的目的),因此将根据语言环境(目前只有英语、法语和德语)自动创建翻译。我们将这个本地化字符串分配给一个标签(我们为其设置了中心位置和填充)。

我们创建两个按钮,一个用于增加状态计数器,另一个用于减少状态计数器,并在on_click()事件发生时进行回调。状态将会增加(减少),Druid 会负责更新界面以反映状态(无需通知标签更新其文本,这是 Druid 的职责……非常棒!)

我们创建一个Flex容器,并将两个按钮水平堆叠,中间留有空间。

然后我们返回另一个Flex包含垂直堆叠标签和Flex按钮的 UI。这就是我们的窗口描述符作为参数的 UI。

我们现在可以运行我们的第一个 Druid 应用程序。

Druid 示例应用程序

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-druid

Rust UI 概要:Druid

返回摘要

弗莱特克

接下来是 FLTK。我承认,第一次用的时候我根本没考虑过它:因为我有一点 C/C++ 背景,所以之前用过 FLTK,说实话,它相当“复杂”,而且效果很差。它不像 wxWidgets 或 QT 那样功能齐全(实际上它更简陋),但它仍然有办法让你写代码……唉!好吧,由于过去的负面经历,我根本没考虑过它。然而,现在有人告诉我,自从我用 C++ 用 FLTK 以来,它已经取得了长足的进步,所以这应该让我明白了偏见的重要性。

事实上,这次我打算尝试列表中的每个 crate,当轮到 FLTK 绑定时,我一尝试就被这个 crate 震撼了:我爱上了它!不知何故,作者设法消除了编写 FLTK 接口的复杂性,并使其具有Rust 风格(请用性感的声音朗读!)。我的意思是,感觉好像它只是 Rust,没有任何绑定。它显然不像是一个包装器,围绕着 FFI 包装器,围绕着 C 库(我认为中文盒子太多了),也不像许多绑定 C 库的 crate(尝试一个 QT crate,然后再回来恳求!)。
相反,它感觉像是真正的 Rust 原生代码。为了做到这一点,有时它会以不同于底层 FLTK 库的方式包装小部件。但我发现这真的很不错。

与 Druid 一样,FLTK 大量采用了构造函数模式。

首先,让我们检查(并且修复)FLTK 的依赖关系

sudo apt-get install libx11-dev libxext-dev libxft-dev libxinerama-dev libxcursor-dev libxrender-dev libxfixes-dev libpango1.0-dev libgl1-mesa-dev libglu1-mesa-dev
Enter fullscreen mode Exit fullscreen mode

然后,我们可以启动一个新项目并声明 FLTK 依赖项:

cargo new rust-ui-fltk
cd rust-ui-fltk
cargo add fltk
Enter fullscreen mode Exit fullscreen mode

让我们看一下src/main.rs

use fltk::{app::*, button::*, frame::*, window::*};

fn main() {
    let app = App::default();
    let mut wind = Window::new(100, 100, 400, 300, "Hello, FLTK!");
    let mut frame = Frame::new(0, 0, 400, 200, "Boring label");
    let mut but = Button::new(160, 210, 80, 40, "Click me!");

    wind.end();
    wind.show();

    // Remember: Callbacks after initializing the interface
    but.set_callback(move || frame.set_label("Hello, World!"));

    app.run().unwrap();
}
Enter fullscreen mode Exit fullscreen mode

我们使用默认设置来设置应用程序,然后为了启动它Window,我们指定 X、Y 位置和宽度、高度尺寸以及标题;然后我们创建一个Frame(相当于 FLTK Box),同样具有位置和尺寸,外加一个标签;最后创建一个按钮(同样具有位置、尺寸和标签)。

我们这里没有使用构造函数模式,但是我们可以对按钮执行以下操作,例如:

let mut but = Button::default()
    .with_pos(160, 210)
    .with_size(80, 40)
    .with_label("Click me!");
Enter fullscreen mode Exit fullscreen mode

相当于上面的Button::new(160, 210, 80, 40, "Click me!")初始化方法。

一旦 UI 完成,我们就完成 UI(使用.end()),然后显示窗口(使用.show())。

一旦 UI 完成,就要设置回调;因此我们为按钮设置一个回调(它在点击事件时被调用),并在其关闭时更改框架的标签。

最后我们只需运行该应用程序。

FLTK 示例应用程序

也许您没有意识到,但这也许是这里最简单的示例,但它功能齐全!

这个箱子非常有前途:作者甚至设法在 Android 系统上使用它

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-fltk

Rust UI 概要:FLTK

返回摘要

GTK

官方GTK绑定,这应该是“严肃”项目的首选。话虽如此,请做好体验不顺畅的准备。要知道,这是一个功能齐全的 crate,绑定到了目前最庞大的三个 GUI 库之一,因此它有一定的复杂性是完全合理的,因为它带来的完全控制能力远超任何其他“简单”的项目。

事实上,还有其他一些 crate 与 GTK 绑定,它们都试图简化 GTK,但却以牺牲完全控制权为代价。这只是一种权衡。

话虽如此,我不得不说,Rust 与 GTK 的绑定比 C/C++/C# 更符合人体工程学。我认为 Rust 中的某些特性使得许多实现比 C 语言的实现更符合人体工程学。不过,不要指望fltk能提供如此优厚的待遇;我们姑且认为这是一种妥协吧。

该项目还提供了使用Glade及其基于 XML 的接口描述符的可能性。

开始时间:

cargo new rust-ui-gtk
cd rust-ui-gtk
cargo add gtk --features=v3_16
cargo add gio --features=v2_44
Enter fullscreen mode Exit fullscreen mode

现在,让我们编写src/main.rs

use gtk::prelude::*;
use gio::prelude::*;
use gtk::{Application, ApplicationWindow, Box, Button, Label};

fn main() {
    let application = Application::new(
        Some("com.github.rust-ui-rundown.rust-ui-gtk"),
        Default::default(),
    ).expect("failed to initialize GTK application");

    application.connect_activate(|app| {
        let window = ApplicationWindow::new(app);
        window.set_title("Hello, GTK+!");
        window.set_default_size(350, 70);

        let container = Box::new(gtk::Orientation::Vertical, 10);

        let label = Label::new(None);
        let button = Button::with_label("Click me!");

        container.add(&label);
        container.add(&button);
        window.add(&container);

        button.connect_clicked(move |_| {
            &label.set_label("Hello, World!");
        });

        window.show_all();
    });

    application.run(&[]);
}
Enter fullscreen mode Exit fullscreen mode

我们首先创建一个应用程序。我们需要向它传递一个标识符(一个Option包装器str)以及应用程序的标志。我们传递的是默认标志。

connect_activate接下来,我们在闭包中调用创建 UI 的方法。在这个闭包中,我们为应用程序声明一个窗口 ( ApplicationWindow),并设置标题和默认尺寸。然后,我们创建一个Box具有垂直方向和间距的容器。

我们设置一个标签,不带任何文本(带有None)和一个按钮。我们将它们添加到容器中;然后将容器本身添加到窗口中。

接下来,我们为按钮声明一个回调,并在按钮被点击后使用一个闭包设置标签文本。

我们通过显示所有小部件来结束闭包connect_activate()。最后,我们运行该应用程序。

该程序按预期运行,并且在这个阶段甚至不太复杂,可以创建像这样的简单 GUI。

GTK 示例应用程序

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-gtk

Rust UI 概要:GTK

返回摘要

冰镇

Iced是一个跨平台、受 Elm 启发的框架,是一个很好的小型解决方案。类似 Elm 的架构已在许多 Rust 项目中使用(例如YewSeed)。与 Druid 一样,Iced 以数据为中心。

第一次运行的时候,我没能运行,因为我没有 Vulkan(而且我在安装过程中用了一些技巧,结果搞砸了:我没有兼容的硬件)。不过,后来 Iced 通过glow获得了一个新的 OpenGL 后端;问题是,crates.io版本还没有这个后端,所以我需要通过 GitHub 仓库来添加它:

cargo new rust-ui-iced
cd rust-ui-iced
cargo add iced --git "https://github.com/hecrj/iced.git" --features glow
Enter fullscreen mode Exit fullscreen mode

现在,让我们转到src/main.rs

use iced::{Element, Sandbox, Settings, Text};

struct MyApp;

impl Sandbox for MyApp {
    type Message = ();

    fn new() -> Self {
        MyApp
    }

    fn title(&self) -> String {
        String::from("Hello, Iced")
    }

    fn update(&mut self, _message: Self::Message) {}

    fn view(&mut self) -> Element<Self::Message> {
        Text::new("Hello, world!").into()
    }
}

pub fn main() -> iced::Result {
    MyApp::run(Settings::default())
}
Enter fullscreen mode Exit fullscreen mode

这个例子稍微简化了一点。我们需要声明一个结构体,将其实现为一个应用程序。在本例中,它是一种Sandboxed应用程序类型,比普通应用程序更简化(并且可用的自定义功能也更少)。

应用程序必须实现在应用程序周期中调用的几个函数:

  • new()初始化应用程序
  • title()指定其标题(可以是动态的:简洁!)
  • update()检查并响应应用程序消息(由Message类型指定,但在本例中为空对象())。这里我们没有任何需要响应的内容。
  • view()渲染应用程序图形部分(UI),在本例中是静止不动的简单文本。

main()我们初始化应用程序,并使用默认选项运行它。

运行时看起来是这样的。

Iced 示例应用程序

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-iced

Rust UI 概要:Iced

返回摘要

宫腔内人工授精

IUI是libui的跨平台包装器,它使用本机框架:常见的 GTK+、cocoa 和 Win32API。

这是一个简单但非常实用的框架,至少对于小型项目来说是这样。让我们通过一个例子来看一下。

cargo new rust-ui-iui
cd rust-ui-iui
cargo add iui
Enter fullscreen mode Exit fullscreen mode

在src/main.rs

use iui::prelude::*;
use iui::controls::{Label, Button, VerticalBox};
use std::rc::Rc;
use std::cell::RefCell;

struct State {
    label: String,
}

fn main() {
    // Initialize the UI and State
    let ui = UI::init().expect("Couldn't initialize UI library");
    let state = Rc::new(RefCell::new(State { label: "".into() }));

    // Create a window into which controls can be placed
    let mut win = Window::new(&ui, "Hello, IUI!", 200, 100, WindowType::NoMenubar);

    // Create a vertical layout to hold the controls
    let mut vbox = VerticalBox::new(&ui);
    vbox.set_padded(&ui, true);

    // Create a new label.
    let label = Label::new(&ui, "");

    // Create a button and its callback
    let mut button = Button::new(&ui, "Click me!");
    button.on_clicked(&ui, {
        let state = state.clone();
        move |_| {
            state.borrow_mut().label = "Hello, world".to_owned();
        }
    });

    // Adding controls to the box, and box to window
    vbox.append(&ui, label.clone(), LayoutStrategy::Stretchy);
    vbox.append(&ui, button.clone(), LayoutStrategy::Compact);

    win.set_child(&ui, vbox);

    // Show the window
    win.show(&ui);

    // Run the application
    let mut event_loop = ui.event_loop();
    event_loop.on_tick(&ui, {
        let ui = ui.clone();
        let mut ui_label = label.clone();
        move || {
            let state = state.borrow();

            // Update all the labels
            ui_label.set_text(&ui, &format!("{}", state.label));
        }
    });
    event_loop.run(&ui);
}
Enter fullscreen mode Exit fullscreen mode

我们首先定义一个结构来保存全局状态,然后将其初始化为RefCell,以便安全地克隆。

我们初始化一个窗口,其中包含一些设置和一个垂直框。然后,我们初始化两个控件:一个标签和一个按钮,并附带其回调。点击按钮后,按钮会改变全局状态,从而更新文本标签。

然后,我们将控件添加到布局中,并将布局添加到窗口。到目前为止,它的工作原理与其他 UI 框架基本相同。

运行应用时,有一点不同:我们必须使用on_tick()事件循环中的回调来管理刷新。
这在概念上类似于MVC 模式,但是,我们将逻辑插入到事件循环本身中。这意味着我们必须将应用的所有更新集中在一个函数中。

这意味着为了将刷新与循环分离,我们需要为每个组件创建一个函数,然后在事件循环中按顺序调用它on_tick。这本身
不是问题,但在构建应用程序架构时必须考虑到这一点。

宫腔内人工授精 (IUI) 示例应用程序

总体而言,我发现 IUI 相当简单,但可用。 遗憾的是,它的发展似乎停滞了。编辑:好消息!正如你在评论中看到的,这个库的开发根本没有停止,只是被底层的 C 库拖慢了速度。

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-iui

Rust UI 概要:IUI

返回摘要

OrbTK

orbtk是Redox 操作系统小部件的基础。它也可以在其他系统上使用(甚至 Android 也支持,但支持尚未完成)。
我不知道是不是我的配置问题,但有时感觉它运行缓慢。即使是很小的改动,编译也需要很长时间,而且每次似乎都要重新编译不少东西。

记得安装xkbwayland作为依赖项:

sudo apt install libxkbcommon-dev libwayland-dev
Enter fullscreen mode Exit fullscreen mode

让我们开始谈正事吧。

cargo new rust-ui-orbtk
cd rust-ui-orbtk
cargo add orbtk --git="https://github.com/redox-os/orbtk.git" --branch develop
Enter fullscreen mode Exit fullscreen mode

至于src/main.rs

use orbtk::prelude::*;

fn main() {
    orbtk::initialize();

    Application::new()
    .window(|ctx| { 
        Window::new()
            .title("Hello, OrbTk!")
            .position((100.0, 100.0))
            .size(200.0, 100.0)
            .child(
                TextBlock::new()
                    .text("Hello, World!")
                    .v_align("center")
                    .h_align("center")
                    .build(ctx)
            )
            .build(ctx)
    })
    .run();
}
Enter fullscreen mode Exit fullscreen mode

这是一个很小的例子:

  • 首先我们初始化orbtk
  • 然后我们创建一个新的Application,并构建它的窗口;
  • window方法获取一个闭包,上下文会被传递给该闭包。在闭包内部,我们创建了一个Window,同样使用了建造者模式。
  • 对于新窗口,我们设置一些属性,例如标题、大小和位置,并将 a 声明TextBlock为其子窗口
  • TextBlock也使用构建器模式构建,设置容器内的对齐方式(垂直和水平)及其文本
  • 并将TextBlock上下文作为初始化小部件的方法Window的参数。build()
  • Application是顶级“容器”,为所包含的小部件提供上下文,它使用一种方法run()来构建和运行。

OrbTK 示例应用程序

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-orbtk

Rust UI 概要:OrbTK

返回摘要

QMetaObject

QMetaObject依赖于 QT Widgets,它的编写是为了直接支持 QML 接口语言,这是一种类似 JSON 的声明性语言,带有一些类似 JavaScript 的结构。

不用说,QT Widgets 是必须安装的。但首先,我们还需要安装一些库(如果你还没有安装的话):

sudo apt install libclang-dev libsqlite3-dev
apt-get install libllvm-11-ocaml-dev libllvm11 llvm-11 llvm-11-dev llvm-11-doc llvm-11-examples llvm-11-runtime
Enter fullscreen mode Exit fullscreen mode

现在我们还需要 QT:

从QT 下载页面下载开源版本:在下一页向下滚动并Download the QT Online Installer下载。

下载后,您必须执行脚本:

./qt-unified-linux-x64-4.0.1-online.run
Enter fullscreen mode Exit fullscreen mode

请安装桌面设置。

此后,我们需要导出路径:

export PATH="~/Qt/5.15.2/gcc_64/bin:$PATH"
export LD_LIBRARY_PATH="~/Qt/5.15.2/gcc_64/lib:$LD_LIBRARY_PATH"
Enter fullscreen mode Exit fullscreen mode

5.15.2用您的实际版本替换。

现在我们可以转到 Rust 端了。

cargo new rust-ui-qmetaobject
cd rust-ui-qmetaobject
cargo add qmetaobject
Enter fullscreen mode Exit fullscreen mode

我们现在可以看到一个根据 repo 的 README 改编的示例main.rsCStr (实际上改编为使用标准):

use std::ffi::CStr;
use qmetaobject::*;

#[allow(non_snake_case)]
#[derive(Default, QObject)]
struct Greeter {
    base : qt_base_class!(trait QObject),
    name : qt_property!(QString; NOTIFY name_changed),
    name_changed : qt_signal!(),
    compute_greetings : qt_method!(fn compute_greetings(&self, verb : String) -> QString {
        return (verb + " " + &self.name.to_string()).into()
    })
}

fn main() {
    qml_register_type::<Greeter>(
        CStr::from_bytes_with_nul(b"Greeter\0").unwrap(),
        1,
        0,
        CStr::from_bytes_with_nul(b"Greeter\0").unwrap()
    );
    let mut engine = QmlEngine::new();
    engine.load_data(r#"
        import QtQuick 2.6; import QtQuick.Window 2.0;
        import Greeter 1.0
        Window {
            visible: true;
            title: "Hello, QMetaObject";
            Greeter { id: greeter; name: 'World'; }
            Text { anchors.centerIn: parent; text: greeter.compute_greetings('hello'); }            
        }
    "#.into());
    engine.exec();
}
Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我们创建了一个自定义类来“实现”一个 C++ 类QObject。当然,Rust结构体无法实现 C++ 对象,因此为了使其正常工作,QMetaObject需要你派生QObject,然后在base字段中使用宏 指定要继承的 C++ 类qt_base_class!(trait QObject)。然后,你可以
结构体中添加任何自定义字段,除了 ,name这是使类能够呈现给底层 C++ 可见的方式。
在本例中,我们添加了一个compute_greetings,它被渲染为 QT 方法,并使用qt_method!()带有闭包的宏。

我们main()需要注册自定义结构体,该结构体qml_register_type接受一个uri、一个主版本号和次版本号(在本例中1.01,0),以及对象的名称(QML 中需要版本号)。uri 和名称都必须以 C 字符串形式传递,在本例中,使用std::ffi::CStr和方法from_bytes_with_nul;以 null 结尾的字符串(完全符合 FFI C++ 风格)则使用 进行渲染\0

现在,该对象已在 QML 中可用,我们可以通过所选的名称和版本(Greeter 1.0在本例中为 )来引用它。
我们可以创建一个 ,QmlEngine并将其加载为 QML 字符串或文件(在本例中为带有 的 Rust 文字字符串#" ... "#)。如您所见,我们在 QML 中使用了 ourGreeter和 its 方法。compute_greetings

QMetaObject 示例应用程序

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-qmetaobject

Rust UI 概要:QMetaObject

返回摘要

雷姆

一个旨在简化 GTK 并赋予其类似 Elm 行为的框架。请查看gtk-rs 的要求/依赖项。

实际上,它并没有简化 GTK,只是让它看起来像 Elm 一样。事实上,你需要知道如何用GTK设计 UI才能使用 Relm。Relm 的一个优点是它也可以兼容Glade 的接口和描述符。另一个优点是,Relm 的示例清晰地展示了如何测试接口,这一点不容小觑!

cargo new rust-ui-relm
cd rust-ui-relm
cargo add relm relm-derive gtk
Enter fullscreen mode Exit fullscreen mode

现在进入src/main.rs

use gtk::{ Button, ButtonExt, ContainerExt, Inhibit, Label, LabelExt, Orientation, WidgetExt, Window, WindowType};
use relm::{connect, Relm, Update, Widget};
use relm_derive::Msg;

struct Model {}

#[derive(Msg)]
enum Msg {
    Hello,
    Quit,
}

#[derive(Clone)]
struct Widgets {
    lbl: Label,
    but: Button,
    window: Window,
}

#[allow(dead_code)]
struct Win {
    model: Model,
    widgets: Widgets,
}

impl Update for Win {
    type Model = Model;
    type ModelParam = ();
    type Msg = Msg;

    fn model(_: &Relm<Self>, _: ()) -> Model {
        Model {}
    }

    fn update(&mut self, event: Msg) {
        let label = &self.widgets.lbl;
        match event {
            Msg::Hello => { label.set_text("Hello, World!"); },
            Msg::Quit => gtk::main_quit(),
        }
    }
}

impl Widget for Win {
    type Root = Window;

    fn root(&self) -> Self::Root {
        self.widgets.window.clone()
    }

    fn view(relm: &Relm<Self>, model: Self::Model) -> Self {
        // Create the view using the normal GTK+ method calls.
        let window = Window::new(WindowType::Toplevel);

        let lbl = Label::new(None);
        let but = Button::with_label("Click Me");

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

        window.add(&vbox);
        window.show_all();

        connect!(relm, but, connect_clicked(_), Msg::Hello);
        connect!(relm, window, connect_delete_event(_, _), return (Some(Msg::Quit), Inhibit(false)));

        Win {
            model,
            widgets: Widgets {
                lbl,
                but,
                window,
            },
        }
    }
}

fn main() {
    Win::run(()).expect("Win::run failed");
}
Enter fullscreen mode Exit fullscreen mode

从本节中可以看出usegtkRelm 并没有真正隐藏在包装器后面,而是处于最前面。

我们需要四个数据结构:

  • 用于保存应用程序的状态(在本例中为无structModel
  • 一个enum Msg用于消息(回调),我们由此导出relm_derive::Msg
  • 用于struct Widgts保存小部件列表(我们需要允许死代码,因为我们没有使用模型,因为它没有状态)
  • 主窗口的结构Win(它包含模型和小部件)

对于Win我们实现的Update循环,设置model()创建模型的函数,以及update()设置每条消息的行为的函数(我们match对这两条消息进行处理,分配gtk::main_quit()消息触发的时间,并在触发消息Quit时设置标签的文本)。Hello

我们还需要实现WidgetWin设置根窗口的类型(在本例中为单窗口gtk::Window)。root()函数用于初始化根窗口,而view()函数则使用窗口小部件和约定来绘制UI gtk

fn view()必须返回已Widget struct初始化的。我们首先声明一个gtk::WindowType::Toplevel窗口。然后声明一个标签(使用text = None)和一个按钮。
我们创建一个容器,并将标签和按钮放入其中,然后将容器作为窗口的根窗口小部件。
显示窗口( )后,我们可以使用有趣的Relmwindow.show_all()设置回调。我们将窗口的退出消息连接到,将按钮的点击连接到connect!Msg::QuitMsg::Hello

运行应用程序后,我们的窗口就会显示出来。

Relm 示例应用程序

您会注意到该界面与基础 GTK 应用程序的界面非常相似。

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-relm

Rust UI 概要:Relm

返回摘要

斯凯特

Sciter是一个由Terra Informatica开发的C-API 商业库,用于基于 Web 技术创建 UI。它(以及许多其他库)都包含 Rust 绑定,即sciter-rs。我们可以使用免费版本,而且似乎也可以在商业软件中使用它。唯一的要求是在某个地方(例如在某个about章节中)包含以下内容:

This Application (or Component) uses Sciter Engine (http://sciter.com/), copyright Terra Informatica Software, Inc.
Enter fullscreen mode Exit fullscreen mode

另一个需要注意的是,该可执行文件可能会向Terra Informatica的某台服务器发送诊断数据。他们非常清楚这一点,而且可能(就我所知)只是诊断性的(这应该不会违反欧盟关于个人数据的保护法……)。总之,重要的是您已经意识到了这一点。

顺便提一下,作者在2020年9月至10月期间在Kikstarter上发起了众筹活动,希望筹集资金用于开源该项目。众筹活动结束后,众筹目标未能达成,但作者开始公开部分替换JavaScript引擎的代码,这些代码遵循BSD三条款许可证,可在github.com/c-smile/sciter-js-sdk上找到。一切看起来都充满希望。

让我们开始谈正事吧...

首先,我们需要从sdk 下载页面下载并解压 sdk。将bin.lnx/x64目录下的libsciter-gtk.so文件复制到 PATH 路径下可访问的文件夹中

如果没有安装,我们还需要安装 GTK 开发文件

sudo apt-get install libgtk-3-dev
Enter fullscreen mode Exit fullscreen mode

现在,讨论 Rust 部分。

cargo new rust-ui-sciter
cd rust-ui-sciter
cargo add sciter-rs
Enter fullscreen mode Exit fullscreen mode

现在来看看我们的src/main.rs

use std::fs;
use std::path::PathBuf;

use sciter;

fn main() {
    let mut frame = sciter::Window::new();
    let html_file_path = PathBuf::from("./app.html");
    let html_file = fs::canonicalize(&html_file_path).unwrap();

    frame.load_file(html_file.to_str().unwrap());
    frame.run_app();
}
Enter fullscreen mode Exit fullscreen mode

笔记:

  • 我们用以下方式创建一个新的窗口框架sciter::Window::new()
  • 为了使用 ,.load_file()我们必须将要加载文件的绝对路径.html(或在线资源的 URL)传递给它。这就是为什么我们费尽心思才得到PathBuf并使用canonicalize它。
  • 最后我们只需运行该应用程序即可run_app()

让我们看看app.html的内容(放在项目的根目录下):

<html>
  <head>
    <title>Hello, Sciter</title>

    <script type="text/tiscript">
      $(button#click).on("click", function() {
        $(h1#label).$append("Hello, world");
      });
    </script>
  </head>
  <body>

    <h1 #label></h1>
    <button #click>Click me!</button>

  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

这很简单。顺便说一句,我们没有探讨 Rust 与 HTML 以及TIscript(Sciter 使用的 JavaScript)之间的互操作性,但这当然是可能的(只是稍微复杂一些)。

Sciter 示例应用程序

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-sciter

Rust UI 概要:Sciter

返回摘要

金牛座

讨论一些重量级的绑定(可能比Sciter还要重)。Tauri可以你为基于 Web 的项目(HTMLCSSJavascript)添加桌面 GUI。那么,它在这个列表中做什么呢?这是一个合理的问题,事实上,我很想把它剔除,就像Rust-QT-Binding-Generator一样。但是,这里有两点需要考虑:

  1. Tauri使用 Rust 将 Web 世界与桌面连接起来,实际上它允许使用 JavaScript 来提供 GUI 的图形端(就像Rust-QT-Binding-Generator使用 QML 一样,而这个留在了列表中)
  2. Tauri连接了 Web 和桌面两种技术,并以 Rust 作为中间人(可能不涉及安全风险!)。由于 Rust 借助 WASM 在 Web 世界中具有良好的渗透性,因此你确实会开始看到Tauri作为 GUI 选项本身的优势(Tauri也与Yew等类似工具配合良好,这意味着可以实现多层级的 Rust 集成)。

我们将Tauri与Vue结合使用,因此您必须首先拥有node/npm/yarn
环境。 编辑:项目移植本身nodejs并非必需(可能只是 HTML/CSS/JS),但目前 CLI 需要它,并且在未来的版本中,我们计划提供纯 Rust CLI。

如果您尚未安装 Vue cli:

yarn global add @vue/cli @vue/cli-service-global
Enter fullscreen mode Exit fullscreen mode

现在让我们也安装 Rust 版本:

cargo install tauri-bundler
Enter fullscreen mode Exit fullscreen mode

最后让我们创建我们的项目(这次使用 Vue!)

vue create rust-ui-tauri
cd rust-ui-tauri
vue add tauri
Enter fullscreen mode Exit fullscreen mode

我们vue add tauri负责项目的 Rust 部分。我们需要回答一些问题,例如应用程序名称(您可以保留默认值)和窗口标题。

然后我们可以用以下方法测试一切:

yarn tauri:serve
Enter fullscreen mode Exit fullscreen mode

我们将在桌面窗口内看到默认的 Vue 主页!

桌面上的 VUE!

现在让我们写一些代码,好吗?

Vue 侧

src/由 Vue 占用,而src-tauri/是 Rust 根。

在src/components/ 目录中,除了已有文件外,我们还添加了另一个.vue文件,名为RustButton.vueHelloWorld.vue

<template>
    <div>
        <h1>{{ msg }}</h1>
        <button @click="call_rust">
            Click Me!
        </button>
    </div>
</template>
<script>
import { promisified } from 'tauri/api/tauri'
export default {
  name: 'RustButton',
  methods: {
    call_rust() {
        promisified({
            cmd: 'getResponse',
            payload: {
                state: 1
            }
        }).then(response => {
            // do something with the Ok() response
            const { message } = response;
            this.msg_set = message;
        }).catch(error => {
            // do something with the Err() string
            console.log(error);
        })
        console.log('Rust invoked with Promisified!');
    }
  },
  data: function () {
    return {
      msg: ''
    }
  },
  computed: {
    msg_set: {
        set: function (newValue) {
        this.msg = newValue
        },
        get: function () {
            return this.msg
        },
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

可能有一个可选<style>部分,但我们不关心这个例子。

包含<template>渲染部分、<script>逻辑。

在 中,<template>我们设置了一个<h1>用于显示变量值的 (双括号作为手柄)。我们还有一个按钮,其@click属性为 ,用于剔除自定义函数。

在部分中,我们tauri<script>导入以使用 Rust 接口,并导出一个包含自定义方法的默认对象,该方法由按钮调用。此方法反过来会调用一个带有有效负载对象的 Rust 函数(它只是为了展示 Rust 和 JS 之间的交互)。这是通过返回一个我们可以使用的JS来实现的(使用自定义计算设置器将数据设置为 Rust 的响应)。当 Rust 响应出现时,仅当响应为 时才会调用该方法;如果是 ,我们可以使用 捕获错误promisifiedcall_rust()getResponsepromisifiedpromise.then()msgResult::OkResult::Err.catch()

如您所见,这是一个一对一匹配的 Rust 函数,它返回一个Result<T, E>.T必须被序列化,而 Tauri 使用 crate 来处理错误anyhow(我们将为其实现一个自定义错误,只是因为......)

还有另一个可以调用 Rust 的 JS 函数,invoke但更适合与 Rust 进行单向通信。

至于 Vue 的其余部分,我们只需要修改src/App.vue来调用我们的自定义组件:

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <br />
    <RustButton msg=""/>
  </div>
</template>

<script>
import RustButton from './components/RustButton.vue'

export default {
  name: 'App',
  components: {
    RustButton
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

<style>我们可以保留原样的部分

实际上<template>,在 中,我们不是调用 ,而是使用空属性<HelloWorld>调用自定义组件。当然,我们需要在 中导入它,并在默认对象中将其作为自定义组件导出。<RustButton>msg</script>

这样我们就处理好了 Vue 方面的问题。

锈蚀面

像往常一样,Rust 的力量非常强大......抱歉,我的意思是:由于 Tauri 已经为我们做了一些脚手架,所以设置一切都很容易。

在src-tauri内部,我们有通常的 Rust 板条箱根,带有Cargo.toml.lock,甚至rustfmt.toml(我将缩进设置为 4 个空格,而不是默认的 2 个,:-))
还有一个 Tauri 的配置文件json

我们感兴趣的是src/cmd.rs。在这里,我们需要放置一些自定义数据结构来与 JS 交互,即Cmd本身(它是一个),它声明了我们在 JS 中设置的enum自定义函数的数据,分别为。我们还必须声明要发送给 JS 的结构体,以及一个表示有效载荷的结构体,该结构体必须模拟我们从 JS 获取的数据。getResponsepayloadcallbackerrorResponse

我们将使用serde,它已经在Cargo.toml中设置为依赖项

use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
pub struct GetResponsePayload {
    pub state: u64,
}

#[derive(Deserialize)]
#[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd {
    GetResponse {
        payload: GetResponsePayload,
        callback: String,
        error: String,
    },
}

#[derive(Serialize)]
pub struct Response<'a> {
    pub message: &'a str,
}
Enter fullscreen mode Exit fullscreen mode

main.rs中,我们定义了一个自定义的 Error 类型(这只是一个样板)。我们更感兴趣的是main()函数:

fn main() {
    tauri::AppBuilder::new()
        .invoke_handler(|_webview, arg| {
            use cmd::Cmd::*;
            match serde_json::from_str(arg) {
                Err(e) => Err(e.to_string()),
                Ok(command) => {
                    match command {
                        // definitions for your custom commands from Cmd here
                        GetResponse {
                            payload,
                            callback,
                            error,
                        } => tauri::execute_promise(
                            _webview,
                            move || {
                                if payload.state != 0 {
                                    let response = cmd::Response {
                                        message: "Hello, World!",
                                    };
                                    Ok(response)
                                } else {
                                    Err(CommandError::new("State not OK").into())
                                }
                            },
                            callback,
                            error,
                        ),
                    }
                    Ok(())
                }
            }
        })
        .build()
        .run();
}
Enter fullscreen mode Exit fullscreen mode

我们使用构建器模式构建一个 Tauri App,然后使用闭包来管理与 JS 的接口,使用invoke_handler()、beforebuild()run()

在闭包内部,我们转换 json 对象处理程序并将match其覆盖:如果是,Ok我们匹配命令以查看我们得到了哪一个(当然,JS 可以调用多个)。

如果是我们的GetResponse,我们就执行承诺,也就是返回答案。我if在从 JS 获取的状态上加了一个守卫,只是为了演示交互……

现在一切设置完毕,我们可以自豪地运行我们的应用程序了:

Tauri 示例应用程序

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-tauri

Rust UI 概要:Tauri

返回摘要

虚拟GTK

VGTK:基于 GTK 和 gtk-rs 构建,位于列表上方,受到 Elm 及其 Rust 对应物的启发,具有各种虚拟 DOM,如 React,以及一种用于 Rust 界面的 JSX......第一次约会要考虑很多事情......好吧,让我们开始这次相亲,看看它是否会是一见钟情......或者如果它将成为一种稳定的友谊,为什么不呢?

对于其他基于 GTK 的框架,我们需要一个可运行的 GTK 环境。

cargo new rust-ui-vgtk
cd rust-ui-vgtk
cargo add vgtk --git=https://github.com/bodil/vgtk
Enter fullscreen mode Exit fullscreen mode

如果我们现在尝试从crates.io版本构建项目,会遇到一些奇怪的编译错误。问题是,crates.iogdk-pixbuf引用的版本号为“0.3.0”的版本号“0.9.0”出现了一些奇怪的问题。相反,从 GitHub 编译后,这些问题就消失了。唯一的问题是编译时间更长……这似乎与默认编译以及文档有关,但我没有时间仔细检查。vgtk

现在,让我们看一下src/main.rs

use vgtk::ext::*;
use vgtk::lib::gio::ApplicationFlags;
use vgtk::lib::gtk::*;
use vgtk::{gtk, run, Component, UpdateAction, VNode};

#[derive(Clone, Debug, Default)]
struct Model {
    msg: String,
}

#[derive(Clone, Debug)]
enum Message {
    Exit,
    UpdateLabel,
}

impl Component for Model {
    type Message = Message;
    type Properties = ();

    fn update(&mut self, msg: Self::Message) -> UpdateAction<Self> {
        match msg {
            Message::Exit => {
                vgtk::quit();
                UpdateAction::None
            },
            Message::UpdateLabel => {
                self.msg = "Hello, world!".to_owned();
                UpdateAction::Render
            }
        }
    }

    fn view(&self) -> VNode<Model> {
        gtk! {
            <Application::new_unwrap(Some("com.example.rustuivgtk"), ApplicationFlags::empty())>
                <Window border_width=20 on destroy=|_| Message::Exit>
                    <HeaderBar title="Hello VGTK!" show_close_button=true />
                        <Box spacing=10 orientation=Orientation::Vertical >
                            <Label label=self.msg.clone() />
                            <Button label="Click Me!" on clicked=|_| Message::UpdateLabel />
                        </Box>
                </Window>
            </Application>
        }
    }
}

fn main() {
    std::process::exit(run::<Model>());
}
Enter fullscreen mode Exit fullscreen mode

老实说,在我看来,它让人想起 VUE 和 Yew 的融合......,VUE 用于代码组织,而 Yew(受 Elm 启发)用于相当可识别的 MVC 模式(这不是经典的 MVC,它有一些特殊之处,我还不能确定......)。

至于代码,它很简单:

我们必须构建一个模型和一个消息系统,就像在这些受 Elm 启发的框架中一样;然后我们需要实现一个模型,该模型具有update()处理消息的功能,以及view()呈现界面的功能。

该模型派生自Default,因此无需初始化(初始化会更加冗长)。
使用view()了 宏gtk!,它类似于 JSX,但适用于 GTK 接口。

您的结果如下:

VGTK 示例应用程序

总的来说,它还不错,结构清晰,处理也gtk!很流畅:它看起来真的像一个基于 Web 的环境,而且我进行的有限测试表明,这个框架足够强大。我一定会深入研究这个框架。

示例回购:

GitHub 徽标 davidedelpapa / rust-ui-vgtk

Rust UI 概要:VGTK

返回摘要


根本不起作用!

阿苏尔

Azul:依赖项损坏了。这个 crate 的开发进度很慢,所以这个 bug 可能很快就会修复。不过,目前它很有前景,但还不能用。

返回摘要

QT_Widgets

QT_Widgets是Ritual的一个 Rust Qt 绑定器,Ritual 是一个 C++ 到 Rust 的包装项目。它的文档不太完善,但它是一系列unsafe针对各种 C++ QT 库的包装器。

我尝试了几种方法让它正常工作,甚至提交了一个问题和一个 bug 的 pull request,但这个 crate 仍然无法正常工作。也许情况会有所改变,我会尽快通知你。

返回摘要


其他问题

公平地说,我在这里收集了一些我甚至没有尝试过或尝试过但存在其他问题的板条箱:

我认为不属于这个名单

也许我有点太固执己见了,但仍然:

Rust-QT-绑定生成器

Rust-QT-Binding-Generator:它为 Rust 和 QT 创建绑定,以便您可以从 QT 界面内部调用 Rust 代码。

主页上说“它有助于在 Rust 代码之上创建基于 Qt 的 GUI”。实际上,你无法在 Rust 程序中使用 QT,而是必须编写一个 QT 程序(用 C++ 和/或 QML)并导入 Rust 代码。

我绝对不会把它当成一个 Rust GUI 项目。它本身很有用,但作为 Rust GUI 来说,它就不是那么有用了。

返回摘要

Web渲染器

AreWeGuiYet上的人们也将WebRender列入了列表中

这不属于 GUI 框架列表,抱歉:它太底层了!现在我们还应该添加 GDK、Cairo 等的绑定……

在这一点上我不同意:你确实可以使用 WebRender 渲染 GUI(就像使用裸 cairo 绑定一样,为什么不呢?),但这并不意味着 WebRender 是一个 GUI 框架。

返回摘要

立即模式“GUI”

我发现了很多立即模式 GUI,我特意将它们从列表中删除:立即模式 GUI 属于游戏世界,而不是桌面应用程序世界。

除非您已经以 Blender 风格完成了 GUI,否则这在图形领域之外也是有用的……在这一点上,让我们讨论一下它!

这里我介绍了两个最常用的(它们存在于AreWeGUIYet中,但不应该存在):

连杆

Conrod与Piston 游戏引擎紧密相关。事实上,它的概念是用户界面。

XCB经常会有一些依赖问题,需要xcb crate的支持。在这种情况下,你可能需要以下部分或全部内容:

sudo apt-get install xcb xcb-proto libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxcb-xrm-dev libxcb1-dev libxkbcommon-dev  libxcb-keysyms1-dev libxcb-xrm0 libxcb-shape0-dev\
libxcb-util0-dev libxcb-cursor-dev  libxkbcommon-x11-dev libxcb-xfixes0-dev   
Enter fullscreen mode Exit fullscreen mode

只需检查链接器抱怨的内容(记住去掉首字母“l”;所以如果是“-lxcb-shape: missing”,你就知道你需要xcb-shape,在中找到libxcb-shape0-dev,等等......)

有效的方法:示例

git clone https://github.com/PistonDevelopers/conrod.git
cd conrod
cargo run --release --example all_winit_glium
Enter fullscreen mode Exit fullscreen mode

示例位于conrod/backends/conrod_glium/examples,如果您有兴趣可以查看一下。

行不通的是:自己制作一个简单的 UI。

现在,我无法重现任何示例并对其进行修改:所有示例都依赖于其中的其他模块,没有任何解释。

还有一份未完成的官方指南,但我还是感到茫然。

没有单一/简单的方法可以理解正在发生的事情、我们需要做什么,以及最重要的,为什么会发生。

我能说什么呢?也许如果你了解 Piston,你就能轻松地在Conrod里畅游,但除此之外,在我看来,它只是胡言乱语。

返回摘要

ImGui

imgui是Dear ImGui的 Rust 绑定……哦,天哪!这和Conrod的做法一模一样:示例文件夹里有很多样板代码,即使你把它们复制到你自己的项目中也行不通!

我说,为什么他们不把所有支持样板都放在项目里?为什么示例中要放一个愚蠢的文件夹?当你还在学习一个新库的时候,就要求一个可以运行的最小示例,是不是太过分了?

无论如何,工作示例如下:

git clone https://github.com/Gekkio/imgui-rs
cd imgui-rs
git submodule update --init --recursive
cd imgui-examples
cargo test
cargo run --example hello_world
cargo run --example test_window
Enter fullscreen mode Exit fullscreen mode

这些项目的状态令人遗憾,但我很好奇:这个包有 70 个贡献者,GitHub repo 有 1.1k 个星。

谁能写个简单的例子、教程或者什么的给我们其他人用?拜托!网上搜索最多只能找到2018年的结果,而且都是些我根本没法尝试的东西,甚至还有404错误!!!

我希望从一个拥有 1.1k 颗星的项目中能够掌握一些东西,哪怕是最少的东西!

返回摘要

无法尝试:核心基础

也许它是 Mac 世界中最好的工具,我不知道,但我无法尝试Core-Foundation,因为我没有 Mac。

返回摘要

无法尝试:LittlevGL

这个有点不同:LittlevGL可以在嵌入式系统上运行。我没能尝试,但我期待着能买个 Raspberry 或类似的设备,在嵌入式系统中尝试一下 Rust。

返回摘要

无法尝试:KAS

我尝试使用 GTK 后端,但它根本不起作用,但主KAS板条箱采用了 Vulkan,而我没有任何兼容的硬件......
我无法尝试,抱歉。

目前,KAS 的绘制方式只有 WebGPU,需要 DirectX 11/12、Vulkan 或 Metal。未来可能会支持 OpenGL 和软件渲染。

我没有 Vulkan 的硬件支持。我尝试了GTK 后端,并在夜间启用 Rust(rustup override set nightly在项目根文件夹中)。可惜没用。

Compiling kas-gtk v0.0.2
error[E0635]: unknown feature `const_vec_new`
 --> /home/<*>/.cargo/registry/src/github.com-1ecc6299db9ec823/kas-gtk-0.0.2/src/lib.rs:8:12
  |
8 | #![feature(const_vec_new)]
  |            ^^^^^^^^^^^^^

error: aborting due to previous error
Enter fullscreen mode Exit fullscreen mode

我希望看到 GTK 后端的改进,这样至少我可以在我的机器上尝试它(或者看到我的薪水有所提高,以便开始拥有更好的机器……但我不认为这很快就会实现)。

返回摘要


结论

过去三个月我尝试了很多不同的 crate。发布之前我甚至还确认它们都能正常工作。

有些 crate 能用,有些则不行。在我看来,那些能用的人应该得到表彰,而那些不能用的人应该得到帮助,以打造出一个最低限度可用的产品。

Rust 生态系统的 GUI 领域如此不堪,真是令人惋惜。诚然,即使在其他语言中,情况也绝非如此简单,但如果 R​​ust 想要脱颖而出,成为主流语言,那么掌控 GUI 状态将带来丰厚的利润。

希望我的这个小作品能够帮助您掌握 Rust GUI 编程。

'下次再见!

文章来源:https://dev.to/davidedelpapa/rust-gui-introduction-aka-the-state-of-rust-gui-libraries-as-of-january-2021-40gl
PREV
Docker 初学者基础知识
NEXT
以下是每个 React 开发人员需要了解的 TypeScript 知识 - 第 1 部分