admin 管理员组

文章数量: 887006

上 github 下载,网址为 https://github/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;  // Optional. Only if the derive macro is used.

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:gabdube/native-windows-gui.git

说明:上面这条指令我无论如何都无法执行。尽管成功设置了 ssh 公钥,也无法成功执行此指令。最终请同时帮我从其他途径下载到此套件源代码。

cd native-windows-gui/native-windows-gui # Running the tests from the workspace screws up the features

cargo test everything --features "all"  # For the test suite
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 文档联机阅读
    • 大量文档,涵盖了您需要了解的有关NWG的所有信息
  • showcase
    • 示例的图像。如果你已经制作了一个NWG应用程序,并想在这里共享它,请给我发一条消息或打开一个PR。这里免费提供场所。

支持的功能

  • 整个winapi控件库(参考)
    • 一些非常特殊的控件不受支持:平面滚动条、ip控件、钢筋和寻呼机。
  • 菜单和菜单栏
  • 图像和字体资源
    • BMP
    • ICO
    • CUR
    • PNG*
    • GIF*
    • JPG*
    • TIFF*
    • DDS*
    • *: Extended image formats with the Windows Imaging Component (WIC).
  • 本地化支持
    • 在内部使用Windows国家语言支持(参考)
  • 工具提示
  • 系统托盘通知
  • 光标处理
  • 完整的剪贴板包装
  • 部分模板支持
    • 将大型应用程序拆分为块
  • 动态控件支持
    • 在运行时添加/删除控件
    • 在运行时绑定或取消绑定新事件
  • 多线程应用程序支持
    • 从另一个线程与GUI线程通信
    • 在不同线程上运行多个窗口
  • 简单布局配置
    • Flexbox布局
    • 网格布局
  • 拖放
    • 将文件从桌面拖放到窗口
  • 最常见的对话框
    • 文件对话框(保存、打开、打开文件夹)
    • “字体”对话框
    • “颜色”对话框
  • 可由外部渲染API使用的画布
  • 高DPI感知
  • 支持辅助功能
    • 选项卡导航
  • 支持低级系统消息捕获(HWND、MSG、WPARAM、LPARAM)
  • 使用Wine和mingw从Linux到Windows的交叉编译和测试。
    • 并非所有功能都支持(但大多数都支持,谢谢WINE!)
    • 参见 https://zork/~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"]
/*!
    A very simple application that shows your name in a message box.
    Unlike `basic_d`, this example uses layout to position the controls in the window
*/


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"]
/**
    A very simple application that show your name in a message box.

    This demo shows how to use NWG without the NativeUi trait boilerplate.
    Note that this way of doing things is alot less extensible and cannot make use of native windows derive.

    See `basic` for the NativeUi version and `basic_d` for the derive version
*/
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"]
/*!
    A very simple application that shows your name in a message box.
    Uses layouts to position the controls in the window
*/

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

}

//
// ALL of this stuff is handled by native-windows-derive
//
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;
            
            // Controls
            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)?;

            // Wrap-up
            let ui = BasicAppUi {
                inner: Rc::new(data),
                default_handler: Default::default(),
            };

            // Events
            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));

           // Layouts
           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 {
        /// To make sure that everything is freed without issues, the default handler must be unbound.
        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();
}

本文标签: 演示 入门 native Rust gui