rust 性能出色,但其gui库并不是很多,成熟的就更少了,而fltk-rs作为fltk rust语言的绑定,基本是够用的。本文描述了一个fltk-rs 编程的一个小例子,代码如下:
main.rs
#![windows_subsystem = “windows”]#[allow(dead_code)]#[warn(unreachable_patterns)]use fltk::{App,enums::{FrameType, *},frame::Frame,image::{SharedImage, SvgImage},prelude::*,window::Window,*,};use fltk::{button::Button,group::Flex,text::{TextBuffer, TextEditor},};use reqwest::Error;// use opener;use std::time::Duration;use std::{process::Command, thread};fn main() {let app = app::App::default().with_scheme(app::Scheme::Gtk);app::background(230, 230, 230);let (width, _height) = app::screen_size();let mut wind = Window::new(100, 100, 800, 600, “fltk test 04”).center_screen();//窗口关闭事件wind.set_callback(|_| {let (width, height) = app::screen_size();if app::event() == enums::Event::Close {let dialog = dialog::choice2(((width / 2.0) as i32) – 200,((height / 2.0) as i32) – 100,“确定退出吗?”,“否”,“是”,“取消”,).unwrap();println!(“dialog: {}”, dialog);if dialog == 1 {app::quit();std::process::exit(0);}}});//图标wind.set_icon(Some(SvgImage::load(“screenshots/logo.svg”).unwrap()));let mut menu = menu::MenuBar::default().with_size(800, 35);menu.set_frame(FrameType::FlatBox);menu.add(“文件/新建\t”,Shortcut::Ctrl | ‘n’,menu::MenuFlag::Normal,menu_cb,);menu.add(“文件/保存\t”,Shortcut::Ctrl | ‘w’,menu::MenuFlag::Normal,menu_cb,);menu.add(“文件/退出\t”,Shortcut::Ctrl | ‘x’,menu::MenuFlag::Normal,menu_cb,);menu.add(“编辑/改写\t”,Shortcut::None,menu::MenuFlag::Normal,menu_cb,);menu.add(“编辑/字句\t”,Shortcut::None,menu::MenuFlag::Normal,menu_cb,);menu.add(“编辑/规则\t”,Shortcut::None,menu::MenuFlag::Normal,menu_cb,);let mut frame = Frame::new(10, 50, 780, 60, “”);frame.set_frame(FrameType::EngravedBox);let mut btn1 = Button::new(20, 70, 60, 30, “@< 开始”);let mut btn2 = Button::new(90, 70, 60, 30, “@>| 结束”);btn2.set_frame(FrameType::GleamUpBox);let flex = Flex::new(220, 70, 120, 30, “”).column();let mut choice_thread = menu::Choice::default().with_label(“多线程”);for i in 1..10 {choice_thread.add_choice(&i.to_string());}//设置默认值choice_thread.set_value(6);//获取默认值println!(“{}”, choice_thread.value() + 1);//监听choice_thread.set_callback(|c| {let v = c.value();match v {v => println!(“{} is selected!”, v + 1),}});flex.end();let mut btn_img = Button::new(350, 60, 340, 40, None);btn_img.set_frame(FrameType::NoBox);// 设置程序图标let mut image = SharedImage::load(“screenshots/logo.png”).unwrap();image.scale(240, 50, true, true);btn_img.set_image(Some(image));// button tooltip messagebtn_img.set_tooltip(“点击访问官方网页”);//打开网址btn_img.set_callback(|_| {let url = “https://www.lanhong-vip.com”;Command::new(“cmd.exe”).args([“/C”, “start”, url]).output().expect(“调用失败”);});// let mut btn3 = Button::new(710,70,60,30,”@<- 退出”);let mut frame2 = Frame::new(10, 120, 780, 320, “”);frame2.set_frame(FrameType::EngravedBox);let flex2 = Flex::new(20, 140, 760, 280, “”).column();let mut text1 = TextEditor::default().center_of(&flex2);let buffer = TextBuffer::default();let buffer2: TextBuffer = buffer.clone();text1.set_buffer(buffer.clone());flex2.end();let width = (width / 1.0) as i32;let mut frame3 = Frame::new(50, 450, 700, 60, “”);frame3.set_frame(FrameType::EngravedBox);let input_url = input::Input::new(100, 465, 400, 30, “网址:”);let mut choice_encode = menu::Choice::new(520, 465, 100, 30, “”);choice_encode.add_choice(“选择编码”);choice_encode.add_choice(“utf-8”);choice_encode.add_choice(“gbk”);choice_encode.set_value(0);let mut btn_submit = button::Button::new(630, 465, 60, 30, “@> 提交”);btn_submit.set_callback(move |btn_submit| {let mut btn_sumbit_clone = btn_submit.clone();let mut value = input_url.value().trim().to_string();if value.len() == 0 {value = “是空的”.to_string();dialog::message_title_default(“提示”);dialog::alert(width / 2 – 200,width / 4 – 100,&format!(“你输入的是:\t{}”, value),);} else {let mut buffer2 = buffer2.clone();btn_sumbit_clone.deactivate();let charset_index = choice_encode.value();let mut charset = “utf-8”;if charset_index == 0 || charset_index == 1 {charset = “utf-8”;} else if charset_index == 2 {charset = “gbk”;}thread::spawn(move || {let res = get_content_by_url(&value, &charset);match res {Ok(r) => buffer2.set_text(&r),Err(e) => buffer2.set_text(&e.to_string()),}btn_sumbit_clone.activate();});}});let flex3 = Flex::default().column().center_of_parent();flex3.end();let mut frame4 = Frame::new(0, 580, width, 320, “”);frame4.set_frame(FrameType::EngravedBox);let mut label1 = Button::new(0, 580, 40, 20, “状态栏”);label1.set_frame(FrameType::NoBox);let mut status_bar_text = Button::new(150, 580, 450, 20, None);status_bar_text.set_frame(FrameType::NoBox);status_bar_text.set_align(Align::Left);status_bar_text.set_label_color(Color::Blue);//必须克隆一个btn2,否则会涉及到多次借用,编译不通过let mut bt2 = btn2.clone();let mut bt1 = btn1.clone();btn1.set_callback(move |btn1| match btn1.value() {false => {let mut b = buffer.clone();let mut status = status_bar_text.clone();let mut text_area = text1.clone();let c = btn1.clone();let mut btn1_1 = btn1.clone();let mut btn2_2 = bt2.clone();bt2.activate();btn1.deactivate();//多线程thread::spawn(move || {for i in 1..10000 {//停止标志if c.active() {break;}let st = “第”.to_owned() + &(i.to_string()) + “个”;status.set_label(&st);b.append(&(i.to_string() + “-“添加生成的内容\n”));thread::sleep(Duration::from_nanos(0));}//一直往下滚动let line = b.text().split(“\n”).count().try_into().unwrap();text_area.scroll(line, 1);btn1_1.activate();btn2_2.deactivate();});}true => {bt2.deactivate();bt1.activate();}});btn2.set_callback(move |btn2| match btn2.value() {false => {btn1.activate();btn2.deactivate();}true => {btn1.deactivate();btn2.activate();}});let flex4 = Flex::new(0, 580, width, 40, “”).column();flex4.end();wind.make_resizable(true);wind.end();wind.show();app.run().unwrap();}/*** 菜单事件监听*/fn menu_cb(m: &mut impl MenuExt) {dialog::message_title_default(“退出程序?”);if let Some(choice) = m.choice() {//匹配每个菜单项let (width, height) = app::screen_size();match choice.as_str() {“新建\t” => println!(“New”),“退出\t” => {//居中显示x,y参数是关键let dialog = dialog::choice2((width / 2.0) as i32 – 200,(height / 2.0) as i32 – 100,“确定退出吗?”,“否”,“是”,“取消”,).unwrap();if dialog == 1 {app::quit();}}_ => println!(“{}”, choice),}}}fn get_content_by_url(url: &str, charset: &str) -> Result<String, Error> {let r = reqwest::blocking::get(url);//异常处理match r {Ok(re) => {let res = re.text_with_charset(charset).unwrap();let document = scraper::Html::parse_document(&res);let title_select = document.root_element();Ok(title_select.html())}Err(e) => Err(e),}}
cargo.toml 内容
[package]name = “test_04”version = “0.1.0”edition = “2021”# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]fltk = “*”reqwest = {version =”0.11″,features = [“blocking”,’json’]}scraper = “*”
最终界面如下:
