上 github 下载,网址为 https://github.com/gabdube/native-windows-gui。
上面有安装说明。按说明方法,老是提示权限不够。配置了 ssh 公钥证书,仍然不行。请教同事后得知,可能是网络传输问题,在国内访问github经常会出现这类问题。于是想办法通过其他方法下载了压缩包。具体用法如下:
Native Windows GUI
欢迎使用 Native Windows GUI(又名NWG)。用于在 Microsoft Windows 桌面上开发本机 GUI 应用程序的 rust 库。
NWG 是 WINAPI 上的轻量级封装。它允许开发人员通过提供一个简单、安全和 Rust风格的接口来处理形形色色的 API 调用。Native Windows GUI 大大简化了开发工作,这意味着更少的编译时间、最少的资源使用、更少的文档搜索时间和更多的应用程序开发时间。
这是 NWG 的第三个也是最后一个版本。它被认为是“成熟的”。这个版本几乎实现了在Windows上开发应用程序所需的所有功能。不要费心使用旧版本,因为它们有“不可调和的设计决策”,并且不能支持一些关键功能。未来的发展将在其他代码库进行。
安装
如果你打算在自己的项目中使用 NWG,需要先把它添加到 cargo.toml
文件中。
[dependencies]
native-windows-gui = "1.0.12"
native-windows-derive = "1.0.3" # Optional. Only if the derive macro is used.
And then, in main.rs or lib.rs :
然后,在 main.rs 或者 lib.rs 文件中添加一下声明:
extern crate native_windows_gui as nwg;
extern crate native_windows_derive as nwd;
Rust 2018 aliasing
通过在 Cargo 中添加以下代码,可以跳过源代码中的 extern crate 定义。toml 注意到,过程宏仍然需要 extern crate 定义,因此这不适用于native-windows-derive。
[dependencies]
nwg = {version = "^1.0.12", package = "native-windows-gui"}
尝试一下
你自己看看吧。NWG 有大量的示例和一个完全交互式的测试套件。您唯一需要做的是:
git clone git@github.com:gabdube/native-windows-gui.git
说明:上面这条指令我无论如何都无法执行。尽管成功设置了 ssh 公钥,也无法成功执行此指令。最终请同时帮我从其他途径下载到此套件源代码。
cd native-windows-gui/native-windows-gui
cargo test everything --features "all"
cargo run --example basic
cargo run --example calculator
cargo run --example message_bank
cargo run --example image_decoder_d --features "extern-canvas"
cargo run --example partials --features "listbox frame combobox"
cargo run --example system_tray --features "tray-notification message-window menu cursor"
cargo run --example dialog_multithreading_d --features "notice"
cargo run --example image_decoder_d --features "image-decoder file-dialog"
cargo run --example month_name_d --features "winnls textbox"
cargo run --example splash_screen_d --features "image-decoder"
cargo run --example drop_files_d --features "textbox"
cd examples/opengl_canvas
cargo run
运行上面的命令,各种各样的例子,应有尽有了,效果非常好!注意执行命令的时候,不要复制 # 号和后面的注释。里面显示 image 图像的例子和最后 OpenGL 的图形展示,是我比较感兴趣的功能。
示例中最接近实际应用程序的内容
cd ../examples/sync-draw
cargo run
这个例子展示了一个简单的画图功能,可以用画笔画图,也可以用橡皮檫檫除绘制内容。
由于嵌入资源,要求控制台以管理员身份运行
cd ../examples/embed_resources
cargo run
从 Ubuntu 系统进行交叉编译
要求安装: MinGW 编译器。
sudo apt install gcc-mingw-w64-x86-64
要求: Rust 支持
rustup target add x86_64-pc-windows-gnu
编译并运行基本例程:
cargo build --release --target=x86_64-pc-windows-gnu
cargo build --release --target=x86_64-pc-windows-gnu --example basic
wine target/x86_64-pc-windows-gnu/release/examples/basic.exe
项目结构
这是git的主要项目。它分为多个部分
- native-windows-gui
- native-windows-derive
- 一个程序宏,它从rust结构生成GUI应用程序(在我看来很酷)
- docs/native-windows-docs 文档联机阅读
- showcase
- 示例的图像。如果你已经制作了一个NWG应用程序,并想在这里共享它,请给我发一条消息或打开一个PR。这里免费提供场所。
支持的功能
- 整个winapi控件库(参考)
- 一些非常特殊的控件不受支持:平面滚动条、ip控件、钢筋和寻呼机。
- 菜单和菜单栏
- 图像和字体资源
- BMP
- ICO
- CUR
- PNG*
- GIF*
- JPG*
- TIFF*
- DDS*
- *: Extended image formats with the Windows Imaging Component (WIC).
- 本地化支持
- 工具提示
- 系统托盘通知
- 光标处理
- 完整的剪贴板包装
- 部分模板支持
- 动态控件支持
- 在运行时添加/删除控件
- 在运行时绑定或取消绑定新事件
- 多线程应用程序支持
- 从另一个线程与GUI线程通信
- 在不同线程上运行多个窗口
- 简单布局配置
- 拖放
- 最常见的对话框
- 文件对话框(保存、打开、打开文件夹)
- “字体”对话框
- “颜色”对话框
- 可由外部渲染API使用的画布
- 高DPI感知
- 支持辅助功能
- 支持低级系统消息捕获(HWND、MSG、WPARAM、LPARAM)
- 使用Wine和mingw从Linux到Windows的交叉编译和测试。
- 并非所有功能都支持(但大多数都支持,谢谢WINE!)
- 参见
https://zork.net/~st/jottings/rust-windows-and-debian.html
l以获取要遵循的步骤
性能
这是在3.40GHz、3401 Mhz、4核、8逻辑处理器的Intel(R)Core(TM)i7-3770 CPU上测量的
在 release 模式下,基本示例的磁盘容量为163kb,内存为900kb。运行时间是瞬时的。
交互式测试套件(包含所有功能和100个测试)的磁盘容量为931kb,内存为8MB。运行时间仍然是瞬时的。
基本应用程序的初始构建时间约为22秒。这主要是由于 winapi 的初始编译时间。随后的编译时间约为0.7秒。
开发
这个图书馆的开发被认为是“完成了”。我的意思是,API不会有任何更改。如果发现错误或文档中的某些区域不清楚,可能会引发问题。如果我忽略了一个非常重要的特性,那么很可能会添加它。
授权许可
NWG使用MIT许可证。
如果说存在“屎上最宽松”的协议,那肯定就是 MIT 协议了!因为它允许你毫无节操地使用开源代码,你可以修改、闭源、商业化等等,MIT只提了两点要求:
- 你使用的时候要保留 MIT 许可声明。
我觉得这个要求不过分,你免费用了人家的东西,产品发行的时候给人家挂个名表示一下感谢总不过分吧? - 出了问题别找作者的麻烦。
这个要求也合情合理。人家没挣你的钱,开源代码是你自己自愿使用的,没人强迫你。如果因为开源代码造成了重大经济损失,你也别找人家赔偿。这个是一个愿打、一个愿挨,都是自己情愿的。
代码例子
With native windows derive
#![windows_subsystem = "windows"]
extern crate native_windows_gui as nwg;
extern crate native_windows_derive as nwd;
use nwd::NwgUi;
use nwg::NativeUi;
#[derive(Default, NwgUi)]
pub struct BasicApp {
#[nwg_control(size: (300, 115), position: (300, 300), title: "Basic example", flags: "WINDOW|VISIBLE")]
#[nwg_events( OnWindowClose: [BasicApp::say_goodbye] )]
window: nwg::Window,
#[nwg_layout(parent: window, spacing: 1)]
grid: nwg::GridLayout,
#[nwg_control(text: "Heisenberg", focus: true)]
#[nwg_layout_item(layout: grid, row: 0, col: 0)]
name_edit: nwg::TextInput,
#[nwg_control(text: "Say my name")]
#[nwg_layout_item(layout: grid, col: 0, row: 1, row_span: 2)]
#[nwg_events( OnButtonClick: [BasicApp::say_hello] )]
hello_button: nwg::Button
}
impl BasicApp {
fn say_hello(&self) {
nwg::modal_info_message(&self.window, "Hello", &format!("Hello {}", self.name_edit.text()));
}
fn say_goodbye(&self) {
nwg::modal_info_message(&self.window, "Goodbye", &format!("Goodbye {}", self.name_edit.text()));
nwg::stop_thread_dispatch();
}
}
fn main() {
nwg::init().expect("Failed to init Native Windows GUI");
nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");
let _app = BasicApp::build_ui(Default::default()).expect("Failed to build UI");
nwg::dispatch_thread_events();
}
简单例子,适用于只需要简单静态UI的情况
#![windows_subsystem = "windows"]
extern crate native_windows_gui as nwg;
use std::rc::Rc;
fn main() {
nwg::init().expect("Failed to init Native Windows GUI");
nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");
let mut window = Default::default();
let mut name_edit = Default::default();
let mut hello_button = Default::default();
let layout = Default::default();
nwg::Window::builder()
.size((300, 115))
.position((300, 300))
.title("Basic example")
.build(&mut window)
.unwrap();
nwg::TextInput::builder()
.text("Heisenberg")
.focus(true)
.parent(&window)
.build(&mut name_edit)
.unwrap();
nwg::Button::builder()
.text("Say my name")
.parent(&window)
.build(&mut hello_button)
.unwrap();
nwg::GridLayout::builder()
.parent(&window)
.spacing(1)
.child(0, 0, &name_edit)
.child_item(nwg::GridLayoutItem::new(&hello_button, 0, 1, 1, 2))
.build(&layout)
.unwrap();
let window = Rc::new(window);
let events_window = window.clone();
let handler = nwg::full_bind_event_handler(&window.handle, move |evt, _evt_data, handle| {
use nwg::Event as E;
match evt {
E::OnWindowClose =>
if &handle == &events_window as &nwg::Window {
nwg::modal_info_message(&events_window.handle, "Goodbye", &format!("Goodbye {}", name_edit.text()));
nwg::stop_thread_dispatch();
},
E::OnButtonClick =>
if &handle == &hello_button {
nwg::modal_info_message(&events_window.handle, "Hello", &format!("Hello {}", name_edit.text()));
},
_ => {}
}
});
nwg::dispatch_thread_events();
nwg::unbind_event_handler(&handler);
}
使用NativeUi样板文件
#![windows_subsystem = "windows"]
extern crate native_windows_gui as nwg;
use nwg::NativeUi;
#[derive(Default)]
pub struct BasicApp {
window: nwg::Window,
layout: nwg::GridLayout,
name_edit: nwg::TextInput,
hello_button: nwg::Button
}
impl BasicApp {
fn say_hello(&self) {
nwg::modal_info_message(&self.window, "Hello", &format!("Hello {}", self.name_edit.text()));
}
fn say_goodbye(&self) {
nwg::modal_info_message(&self.window, "Goodbye", &format!("Goodbye {}", self.name_edit.text()));
nwg::stop_thread_dispatch();
}
}
mod basic_app_ui {
use native_windows_gui as nwg;
use super::*;
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
pub struct BasicAppUi {
inner: Rc<BasicApp>,
default_handler: RefCell<Option<nwg::EventHandler>>
}
impl nwg::NativeUi<BasicAppUi> for BasicApp {
fn build_ui(mut data: BasicApp) -> Result<BasicAppUi, nwg::NwgError> {
use nwg::Event as E;
nwg::Window::builder()
.flags(nwg::WindowFlags::WINDOW | nwg::WindowFlags::VISIBLE)
.size((300, 115))
.position((300, 300))
.title("Basic example")
.build(&mut data.window)?;
nwg::TextInput::builder()
.text("Heisenberg")
.parent(&data.window)
.focus(true)
.build(&mut data.name_edit)?;
nwg::Button::builder()
.text("Say my name")
.parent(&data.window)
.build(&mut data.hello_button)?;
let ui = BasicAppUi {
inner: Rc::new(data),
default_handler: Default::default(),
};
let evt_ui = Rc::downgrade(&ui.inner);
let handle_events = move |evt, _evt_data, handle| {
if let Some(ui) = evt_ui.upgrade() {
match evt {
E::OnButtonClick =>
if &handle == &ui.hello_button {
BasicApp::say_hello(&ui);
},
E::OnWindowClose =>
if &handle == &ui.window {
BasicApp::say_goodbye(&ui);
},
_ => {}
}
}
};
*ui.default_handler.borrow_mut() = Some(nwg::full_bind_event_handler(&ui.window.handle, handle_events));
nwg::GridLayout::builder()
.parent(&ui.window)
.spacing(1)
.child(0, 0, &ui.name_edit)
.child_item(nwg::GridLayoutItem::new(&ui.hello_button, 0, 1, 1, 2))
.build(&ui.layout)?;
return Ok(ui);
}
}
impl Drop for BasicAppUi {
fn drop(&mut self) {
let handler = self.default_handler.borrow();
if handler.is_some() {
nwg::unbind_event_handler(handler.as_ref().unwrap());
}
}
}
impl Deref for BasicAppUi {
type Target = BasicApp;
fn deref(&self) -> &BasicApp {
&self.inner
}
}
}
fn main() {
nwg::init().expect("Failed to init Native Windows GUI");
nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");
let _ui = BasicApp::build_ui(Default::default()).expect("Failed to build UI");
nwg::dispatch_thread_events();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)