Rust GUI:简介,也就是 Rust GUI 库的现状(截至 2021 年 1 月)
自述文件
在职的
根本不起作用!
其他问题
结论
[ 图片由 Kelly Sikkema 在 Unsplash 上拍摄,已修改(裁剪)]
本文是一篇讨论的后续文章。请分享你的经验,我很乐意听听!
介绍
几个月前,我调查了一下 Rust 的 GUI 现状。说实话,调查结果很糟糕。
从互联网上进行一些搜索开始,除了 Reddit 和其他网站上的一些问题之外,您还会得到一些(非常旧的)文章。
自从几个月前得知 Rust 的存在以来,我一直对它非常感兴趣。然而,当时让我望而却步的一件事是它缺乏跨平台设备的(结构化) GUI 绑定。但几个月后,有没有一个好的 GUI 库可以用来开发“复杂”的应用程序?或者……
又老又陈旧。
最终,您获得的唯一指南(供所有人参考)是 Are We GUI Yet 网站,但它根本没有帮助。
自述文件
想找 Rust 的 GUI 库吗?那你来对地方了!
地位
本网站已尽力维护。下一步计划可能将其替换为类似 lib.rs 的 自动化系统。
如果此 repo 中的 crate 配置未覆盖 CI,则 CI 每周运行一次以更新来自 crates.io 的 crate 信息。
这是什么?
arewegameyet 、 arewewebyet 和 arewelearningyet 的配套网站 。
贡献
让人们轻松(嘿,那就是你!
😊
) 贡献,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 add
cargo edit (来安装它:) cargo install cargo-edit
,但你可以直接将依赖项添加到 Cargo.toml
我尽一切可能让这些 crate 正常工作,而且我是认真的。我甚至在本地打了补丁,提交了一些问题,甚至还提交了 PR。我真心实意地做好了功课。
摘要表
以下是我尝试过的板条箱的摘要,以及我发现它们的状态:
还有两个条目我完全排除了,因为我认为它们不属于 Rust GUI: WebRender 和 Rust-QT-Binding-Generator。Tauri 也差点被踢出局,但它保留了下来,因为它完全是另一个 “ 野兽”(因为它是技术之间的桥梁,而不是像 Rust-QT-Binding-Generator 那样是语言之间的桥梁)。
在职的
德鲁伊
我第一次尝试的时候没能用上其中一个 crate(我不记得为什么),但现在 Druid 已经完美运行了。 它依赖于 piet ,一个 2D 图形库。在 Linux 上,它封装了 GTK3 及其 cairo 2D 基元,所以请检查你的发行版是否满足所有 要求 。 它还有一个基于 Web 的渲染引擎,并使用 wasm 和 wasm-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()
一个新的主窗口描述符 。我们设置主窗口的标题和状态。 WindowDesc
ui_builder()
我们初始化自定义状态( Data
用德鲁伊的术语来说),并将窗口和状态传递给 AppLauncher
,它将为我们管理应用程序的周期。
请注意,Druid 实现了一个简单的记录器 stderr
,我认为这对于调试来说是一个简洁的功能。
在 中, fn ui_builder()
我们首先创建一个本地化字符串 ( LocalizedString
),用于渲染可翻译的文本。此外,这是一个很好的函数,可以将本地化嵌入到常规开发流程中。我们 "hello-counter"
为其分配键;由于这是默认捆绑的(例如,为了示例的目的),因此将根据语言环境(目前只有英语、法语和德语)自动创建翻译。我们将这个本地化字符串分配给一个标签(我们为其设置了中心位置和填充)。
我们创建两个按钮,一个用于增加状态计数器,另一个用于减少状态计数器,并在 on_click()
事件发生时进行回调。状态将会增加(减少),Druid 会负责更新界面以反映状态(无需通知标签更新其文本,这是 Druid 的职责……非常棒!)
我们创建一个 Flex
容器,并将两个按钮水平堆叠,中间留有空间。
然后我们返回另一个 Flex
包含垂直堆叠标签和 Flex
按钮的 UI。这就是我们的窗口描述符作为参数的 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 完成,就要设置回调;因此我们为按钮设置一个回调(它在点击事件时被调用),并在其关闭时更改框架的标签。
最后我们只需运行该应用程序。
也许您没有意识到,但这也许是这里最简单的示例,但它功能齐全!
这个箱子非常有前途: 作者甚至设法在 Android 系统上使用它 。
示例回购:
返回摘要
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。
示例回购:
返回摘要
冰镇
Iced 是一个跨平台、受 Elm 启发的框架, 是一个很好的小型解决方案。类似 Elm 的架构已在许多 Rust 项目中使用(例如 Yew 或 Seed )。与 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()
我们初始化应用程序,并使用默认选项 运行它。
运行时看起来是这样的。
示例回购:
返回摘要
宫腔内人工授精
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 相当简单,但可用。
遗憾的是,它的发展似乎停滞了。 编辑:好消息!正如你在评论中看到的,这个库的开发根本没有停止,只是被底层的 C 库拖慢了速度。
示例回购:
返回摘要
OrbTK
orbtk是 Redox 操作系统 小部件的基础 。它也可以在其他系统上使用(甚至 Android 也支持,但支持尚未完成)。 我不知道是不是我的配置问题,但有时感觉它运行缓慢。即使是很小的改动,编译也需要很长时间,而且每次似乎都要重新编译不少东西。
记得安装 xkb 和 wayland 作为依赖项:
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()
来构建和运行。
示例回购:
返回摘要
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.rs CStr
(实际上改编为使用标准):
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.0
为 1,0
),以及对象的名称(QML 中需要版本号)。uri 和名称都必须以 C 字符串形式传递,在本例中,使用 std::ffi::CStr
和方法 from_bytes_with_nul
;以 null 结尾的字符串(完全符合 FFI C++ 风格)则使用 进行渲染 \0
。
现在,该对象已在 QML 中可用,我们可以通过所选的名称和版本( Greeter 1.0
在本例中为 )来引用它。 我们可以创建一个 , QmlEngine
并将其加载为 QML 字符串或文件(在本例中为带有 的 Rust 文字字符串 #" ... "#
)。如您所见,我们 在 QML 中使用了 our Greeter
和 its 方法。 compute_greetings
示例回购:
返回摘要
雷姆
一个旨在简化 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
从本节中可以看出 use
, gtk
Relm 并没有真正隐藏在包装器后面,而是处于最前面。
我们需要四个数据结构:
用于 保存应用程序的状态(在本例中为无 struct
) Model
一个 enum
Msg
用于消息(回调),我们由此导出 relm_derive::Msg
用于 struct
Widgts
保存小部件列表(我们需要允许死代码,因为我们没有使用模型,因为它没有状态)
主窗口的结构 Win
(它包含模型和小部件)
对于 Win
我们实现的 Update
循环,设置 model()
创建模型的函数,以及 update()
设置每条消息的行为的函数(我们 match
对这两条消息进行处理,分配 gtk::main_quit()
消息触发的时间,并在 触发消息 Quit
时设置标签的文本)。 Hello
我们还需要实现 Widget
, Win
设置根窗口的类型(在本例中为单窗口 gtk::Window
)。 root()
函数用于初始化根窗口,而 view()
函数则使用窗口小部件和约定来绘制UI gtk
。
fn view()
必须返回已 Widget
struct
初始化的。我们首先声明一个 gtk::WindowType::Toplevel
窗口。然后声明一个标签(使用 text = None
)和一个按钮。 我们创建一个容器,并将标签和按钮放入其中,然后将容器作为窗口的根窗口小部件。 显示窗口( )后,我们可以使用有趣的 Relm 宏 window.show_all()
设置回调 。我们将窗口的退出消息连接到 ,将按钮的点击连接到 。 connect!
Msg::Quit
Msg::Hello
运行应用程序后,我们的窗口就会显示出来。
您会注意到该界面与基础 GTK 应用程序的界面非常相似。
示例回购:
返回摘要
斯凯特
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 还要重)。Tauri可以 让 你为基于 Web 的项目( HTML
、 CSS
和 Javascript
)添加桌面 GUI。那么,它在这个列表中做什么呢?这是一个合理的问题,事实上,我很想把它剔除,就像 Rust-QT-Binding-Generator 一样。但是,这里有两点需要考虑:
Tauri 使用 Rust 将 Web 世界与桌面连接起来,实际上它允许使用 JavaScript 来提供 GUI 的图形端(就像 Rust-QT-Binding-Generator 使用 QML 一样,而这个留在了列表中)
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 侧
src/ 由 Vue 占用,而 src-tauri/ 是 Rust 根。
在src/components/ 目录 中,除了 已有文件外, 我们还添加了另一个 .vue
文件,名为 RustButton.vue : HelloWorld.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 响应出现时,仅当响应为 时才会调用该方法 ;如果是 , 我们可以使用 捕获错误 。 promisified
call_rust()
getResponse
promisified
promise
.then()
msg
Result::Ok
Result::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 获取的数据。 getResponse
payload
callback
error
Response
我们将使用 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()
、before build()
和 run()
。
在闭包内部,我们转换 json 对象处理程序并将 match
其覆盖:如果是, Ok
我们匹配命令以查看我们得到了哪一个(当然,JS 可以调用多个)。
如果是我们的 GetResponse
,我们就执行承诺,也就是返回答案。我 if
在从 JS 获取的状态上加了一个守卫,只是为了演示交互……
现在一切设置完毕,我们可以自豪地运行我们的应用程序了:
示例回购:
返回摘要
虚拟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.io 中 gdk-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 接口。
您的结果如下:
总的来说,它还不错,结构清晰,处理也 gtk!
很流畅:它看起来真的像一个基于 Web 的环境,而且我进行的有限测试表明,这个框架足够强大。我一定会深入研究这个框架。
示例回购:
返回摘要
根本不起作用!
阿苏尔
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 领域如此不堪,真是令人惋惜。诚然,即使在其他语言中,情况也绝非如此简单,但如果 Rust 想要脱颖而出,成为主流语言,那么掌控 GUI 状态将带来丰厚的利润。
希望我的这个小作品能够帮助您掌握 Rust GUI 编程。
'下次再见!
文章来源:https://dev.to/davidedelpapa/rust-gui-introduction-aka-the-state-of-rust-gui-libraries-as-of-january-2021-40gl