Rust 语言计算器界面设计





5.00/5 (4投票s)
本技巧描述了如何使用 Rust 编程语言中的 Win32 API 设计应用程序界面。
引言
在本技巧中,我们将使用纯粹的 Win32 API 函数在 Rust 编程语言中设计一个简单的计算器图形界面。
在 Code Project 上,我写了一篇关于 Rust 的文章,名为“使用 Rust 语言进行 Win32 GUI 编程”,其中我展示了如何使用纯 Win32 API 创建一个简单的窗口。 尽管存在一些错误,但一些 Code Project 成员通过评论帮助我纠正了这些错误。 所以我想对他们表示衷心的感谢。
Using the Code
使用纯 Win32 API 设计图形用户界面有点困难。 但最终,它对理解基本概念非常有帮助。 因此,本技巧将帮助您理解基本图形元素(如按钮、标签等)的用途,以设计应用程序的用户界面。 并且,我们还将看到如何处理按钮的点击事件并更改标签控件的背景颜色。
首先,我们必须 extern
我们必要的 crate(这些 crate 可在互联网上找到)
extern crate kernel32;
extern crate user32;
extern crate gdi32;
extern crate winapi;
extern crate libc;
use winapi::windef::HWND;
use winapi::windef::HMENU;
use winapi::windef::HBRUSH;
use winapi::minwindef::HINSTANCE;
use winapi::minwindef::UINT;
use winapi::minwindef::DWORD;
use winapi::minwindef::WPARAM;
use winapi::minwindef::LPARAM;
use winapi::minwindef::LRESULT;
use winapi::winnt::LPCWSTR;
use winapi::winuser::WS_VISIBLE;
use winapi::winuser::WS_CHILD;
use winapi::winuser::WS_EX_CLIENTEDGE;
use winapi::winuser::WNDCLASSW;
use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;
static mut op_e1 :i32 = 0;
static mut op_e2 :i32 = 0;
static mut op_operator :i32 = 0;
static mut hwnd_display :HWND = 0 as HWND;
我们需要一个 Rust 的 string
到 utf16 string
转换器函数来使用 Win32 API 函数
fn to_wstring(str : &str) -> Vec<u16> {
let v : Vec<u16> =
OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
v
}
以下函数是按钮的点击事件处理程序。 这些函数从 WM_COMMAND
消息中被调用,以处理计算器按钮的点击事件。 尽管,目前这些函数并没有做任何重要的事情
unsafe fn on_clear_click() {
op_e1 = 0;
op_e2 = 0;
op_operator = 0;
user32::SetWindowTextW(hwnd_display, to_wstring("0").as_ptr());
}
unsafe fn on_numbers_click(id :u32) {
// Here our numbers_click event codes
user32::SetWindowTextW(hwnd_display, to_wstring("Not Implemented!").as_ptr());
}
unsafe fn on_operators_click() {
op_operator = 1;
user32::SetWindowTextW(hwnd_display, to_wstring("0").as_ptr());
}
unsafe fn on_equal_click() {
let result = op_e1 + op_e2;
let res_str: String = result.to_string();
let ws = to_wstring(&res_str[..]).as_ptr();
user32::SetWindowTextW(hwnd_display, ws);
}
这是我们窗口的消息处理程序函数。 在这里,我们处理 WM_DESTROY
、WM_CTLCOLORSTATIC
和 WM_COMMAND
消息
pub unsafe extern "system" fn window_proc
(h_wnd :HWND, msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT
{
if msg == winapi::winuser::WM_DESTROY {
user32::PostQuitMessage(0);
}
我们处理 WM_CTLCOLORSTATIC
以将显示标签控件的背景颜色更改为白色
else if msg == winapi::winuser::WM_CTLCOLORSTATIC
{
if hwnd_display == (l_param as HWND)
{
let h_brush = gdi32::CreateSolidBrush((255 | (255 << 8)) | (255 | (255 << 16)));
return h_brush as LRESULT;
}
}
并且我们还处理 WM_COMMAND
以检测按钮点击事件。 尽管,它在这里没有做任何重要的事情,但肯定会帮助您理解按钮点击事件的处理。
else if msg == winapi::winuser::WM_COMMAND
{
我知道这并不是一个好方法,但我发现它更容易
if w_param > 100 && w_param < 111 // Range of number buttons id
{
on_numbers_click(w_param as u32);
}
else if w_param > 110 && w_param < 117 // Range of operator buttons id
{
if w_param == 115 {
on_clear_click();
}
else {
on_operators_click();
}
}
else if w_param == 117 // Id of '=' operator button
{
on_equal_click();
}
}
return user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
}
由于我们将创建一个计算器应用程序的界面,因此我们需要大量的按钮和一个标签控件。 为了创建计算器的按钮控件和其他图形元素,我们将使用 CreateWindowExW
函数。 作为计算器的显示控件,我们将使用一个静态/标签控件。 我们将在一个名为 init_interface
的函数中创建所有控件
unsafe fn init_interface(h_wnd :HWND)
{
hwnd_display = user32::CreateWindowExW(WS_EX_CLIENTEDGE, to_wstring("static").as_ptr(),
to_wstring("0"), WS_CHILD | WS_VISIBLE | 2 /*SS_RIGHT*/,
46, 20, 256, 60, h_wnd, 340 as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
let mut i = 1;
let mut x = 46;
let mut y = 210;
let mut btn_num_id = 101;
现在,我们使用循环语句创建所有数字按钮。 这将使事情变得更容易
loop
{
if i >= 10 {
break;
}
let txt = format!("{}", i);
user32::CreateWindowExA(0, "button".as_ptr() as *mut _,
txt.as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
x = x + 54;
if i % 3 == 0 {
x = 46;
y = y - 54;
}
btn_num_id = btn_num_id + 1;
i = i + 1;
}
让我们创建所有算术计算按钮,例如“+”、“-”等
x = 46;
y = 264;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"0".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 148, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
btn_num_id = btn_num_id + 1;
x = 208;
y = 102;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"+".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
y = y + 54;
btn_num_id = btn_num_id + 1;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"-".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
y = y + 54;
btn_num_id = btn_num_id + 1;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"x".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
y = y + 54;
btn_num_id = btn_num_id + 1;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"/".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
btn_num_id = btn_num_id + 1;
x = 262;
y = 102;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"C".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
y = y + 54;
btn_num_id = btn_num_id + 1;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"%".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 32, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
y = y + 54;
btn_num_id = btn_num_id + 1;
user32::CreateWindowExA(0, "Button".as_ptr() as *mut _,
"=".as_ptr() as *mut _, WS_CHILD | WS_VISIBLE,
x, y, 40, 86, h_wnd, btn_num_id as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
}
这是我们程序的入口点函数。 在这里,我们注册并创建我们的主窗口。 然后,我们调用 init_interface 函数来创建我们的计算器界面(静态和按钮)。
fn main()
{
unsafe
{
let class_name = to_wstring("my_window");
let wnd = WNDCLASSW {
style: 0,
lpfnWndProc: Some(window_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: 0 as HINSTANCE,
hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
hbrBackground: 16 as HBRUSH,
lpszMenuName: 0 as LPCWSTR,
lpszClassName: class_name.as_ptr(),
};
user32::RegisterClassW(&wnd);
let h_wnd_main = user32::CreateWindowExW(0, class_name.as_ptr(),
to_wstring("Simple Calculator Interface In Rust").as_ptr(),
winapi::winuser::WS_OVERLAPPED | winapi::winuser::WS_CAPTION |
winapi::winuser::WS_SYSMENU | winapi::winuser::WS_MINIMIZEBOX | WS_VISIBLE,
0, 0, 366, 400, 0 as HWND, 0 as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
init_interface(h_wnd_main);
user32::ShowWindow(h_wnd_main, winapi::SW_SHOW);
let mut msg = winapi::winuser::MSG {
hwnd : 0 as HWND,
message : 0 as UINT,
wParam : 0 as WPARAM,
lParam : 0 as LPARAM,
time : 0 as DWORD,
pt : winapi::windef::POINT { x: 0, y: 0, },
};
// Finally we run the standard application loop -
loop {
let pm = user32::GetMessageW(&mut msg, 0 as HWND, 0, 0);
if pm == 0 {
break;
}
if msg.message == winapi::winuser::WM_QUIT {
break;
}
user32::TranslateMessage(&mut msg);
user32::DispatchMessageW(&mut msg);
}
}
}
我们的 Cargo.toml 文件的内容将如下所示(name、version 和 authors 由您选择)
[package]
name = "simple_calculator_interface"
version = "0.0.1"
authors = [ "Your name <you@example.com>" ]
[dependencies]
libc = "0.1.10"
winapi = "0.2.4"
user32-sys = "0.1.2"
kernel32-sys = "0.1.4"
gdi32-sys = "0.1.1"
使用以下命令编译程序(非常感谢 Code Project 成员,他帮助我通过命令行参数删除了黑色的控制台窗口)
cargo rustc -- -C link-args=-mwindows
现在运行您的 'simple_calculator_interface.exe'。
结论
我希望本技巧能够帮助程序员理解 Rust 语言中的基本 GUI 编程。 虽然我知道直接使用 Win API 并不是最好的主意。 但是,它确实有助于理解基本思想。