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( ? ? ? ? "編輯/規(guī)則\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); ? ? //監(jiān)聽 ? ? 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 message ? ? btn_img.set_tooltip("點擊訪問官方網頁"); ? ? //打開網址 ? ? btn_img.set_callback(|_| { ? ? ? ? let url = "http://www.fdftxzl.com.cn"; ? ? ? ? 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, "狀態(tài)欄"); ? ? 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(); } /** ?* 菜單事件監(jiān)聽 ?*/ 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參數(shù)是關鍵 ? ? ? ? ? ? ? ? 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 = "*"
最終界面如下:
