本示例展示了基于 FLTK 和 SQLITE 制作用户管理程序。代码比较长,但是不难看懂。

main.rs

mod store {
    use super::Userdata;
    use anyhow::Result;
    use rusqlite::Connection;

    pub struct Database {
        conn: Connection,
    }

    impl Database {
        pub fn create_connection() -> Result<Self> {
            let conn = Connection::open(":memory:")?;

            conn.execute(
                r#"CREATE TABLE IF NOT EXISTS userdata (
                id INTEGER PRIMARY KEY,
                username TEXT NOT NULL,
                age INTEGER NOT NULL
            );"#,
                (),
            )?;

            Ok(Self { conn })
        }

        pub fn insert_userdata(&self, username: &str, age: u64) -> Result<()> {
            self.conn.execute(
                r#"INSERT INTO userdata (username, age) VALUES (?, ?);"#,
                (username, age),
            )?;
            Ok(())
        }

        pub fn insert_many_userdata(&mut self, count: u64, username: &str, age: u64) -> Result<()> {
            let tx = self.conn.transaction()?;
            for _ in 0..count {
                tx.execute(
                    r#"INSERT INTO userdata (username, age) VALUES (?, ?);"#,
                    (username, age),
                )?;
            }
            tx.commit()?;
            Ok(())
        }

        pub fn update_userdata(&self, id: u64, username: &str, age: u64) -> Result<()> {
            self.conn.execute(
                r#"UPDATE userdata
                SET username = ?, age = ?
                WHERE id=?;"#,
                (username, age, id),
            )?;
            Ok(())
        }

        pub fn delete_userdata(&self, id: u64) -> Result<()> {
            self.conn
                .execute(r#"DELETE FROM userdata WHERE id=?;"#, (id,))?;
            Ok(())
        }

        pub fn get_all_userdata(&self, limit: u64, offset: u64) -> Result<Vec<Userdata>> {
            let mut stmt = self
                .conn
                .prepare("SELECT id, username, age FROM userdata limit ? offset ?")?;
            let person_iter = stmt.query_map([limit, offset], |row| {
                Ok(Userdata {
                    id: row.get(0)?,
                    username: row.get(1)?,
                    age: row.get(2)?,
                })
            })?;

            let mut result = Vec::new();
            for person in person_iter {
                result.push(person?);
            }

            Ok(result)
        }

        pub fn get_count(&self) -> Result<u64> {
            let mut stmt = self.conn.prepare("SELECT COUNT(*) FROM userdata;")?;
            let rows = stmt.query([])?.and_then(|row| {
                let count: u64 = row.get(0)?;
                anyhow::Ok(count)
            });

            for row in rows {
                return Ok(row?);
            }
            Ok(0)
        }
    }
}

use fltk::{
    app::{channel, App},
    dialog::{alert_default, input_default},
};

#[derive(Clone, Debug, Default)]
pub struct Userdata {
    pub id: u64,
    pub username: String,
    pub age: u64,
}

mod ui {

    use super::Userdata;
    use fltk::app::Sender;
    // use fltk::prelude::WidgetExt;

    fl2rust_macro::include_ui!("ui/main.fl");

    #[derive(Clone)]
    pub enum Message {
        Insert,
        QueryAll,
        SelectUser,
        UpdateUser,
        DeleteUser,
        SetRows,
        Before,
        Next,
        Refresh,
        InsertMany,
        SetPage
    }

    trait SenderForAppUi {
        fn bind<W>(&self, widget: &mut W, message: Message)
        where
            W: WidgetExt + Sized;
    }

    impl SenderForAppUi for Sender<Message> {
        fn bind<W>(&self, widget: &mut W, message: Message)
        where
            W: WidgetExt + Sized,
        {
            widget.emit(self.clone(), message);
        }
    }

    macro_rules! emit {
        ($sender:expr, {$($widget:expr => $message:expr),+}) => {
            $(
                let sender = $sender.clone();
                $widget.set_callback(move |_| sender.send($message));
            )+
        };
    }

    impl UserInterface {
        pub fn emit(&mut self, sender: Sender<Message>) {
            emit!(sender, {
                self.btn_insert_new => Message::Insert,
                self.btn_insert_to_select => Message::UpdateUser,
                self.btn_delete_select => Message::DeleteUser,
                self.btn_refresh =>  Message::Refresh,
                self.btn_set_rows => Message::SetRows,
                self.btn_go_before => Message::Before,
                self.btn_go_next =>  Message::Next,
                self.btn_many => Message::InsertMany,
                self.sql_browser => Message::SelectUser
            });
        }

        pub fn get_edit_userdata(&self) -> Userdata {
            Userdata {
                id: self.input_id.value().parse::<u64>().unwrap_or(0),
                username: self.input_username.value(), 
                age: self.input_age.value().parse::<u64>().unwrap_or(0),
            }
        }

        pub fn set_edit_userdata(&mut self, userdata: &Userdata) {
            self.input_username.set_value(&userdata.username);
            self.input_age.set_value(userdata.age.to_string().as_str());
            self.input_id.set_value(userdata.id.to_string().as_str());
        }

        fn sql_browser_prefare(&mut self) {
            self.sql_browser.clear();
            self.sql_browser.set_text_size(16);
            let widths = &[50, 100, 50];
            self.sql_browser.set_column_widths(widths);
            self.sql_browser.set_column_char('\t');
            self.sql_browser.add("ID\tUSERNAME\tAGE");
        }

        fn sql_browser_add_data(&mut self, userdata: &Userdata) {
            self.sql_browser.add_with_data(
                &format!("{}\t{}\t{}", userdata.id, userdata.username, userdata.age),
                userdata.clone(),
            );
        }

        pub fn load_query_data(&mut self, userdata: &[Userdata]) {
            self.sql_browser_prefare();
            for user in userdata {
                self.sql_browser_add_data(&user);
            }
        }

        pub fn get_select_userdata(&self) -> Option<Userdata> {
            let index = self.sql_browser.selected_items();
            if index.len() > 0 {
                let data: Option<Userdata> = unsafe { self.sql_browser.data(index[0]) };
                return data;
            }
            None
        }

        pub fn set_page(&mut self, page: u64) {
            self.op_page.set_value(page.to_string().as_str());
        }
    }
}

use anyhow::Result;
use store::Database;
use ui::{Message, UserInterface};

fn manage_error_in_alert<T>(result: Result<T>) {
    if let Err(error) = result {
        alert_default(error.to_string().as_str())
    }
}

fn main() {
    let app = App::default();
    let (s, r) = channel::<Message>();
    let mut app_ui = UserInterface::make_window();
    let mut database = Database::create_connection().unwrap();
    app_ui.emit(s.clone());
    s.send(Message::QueryAll);

    let mut limit = 10;
    let mut page = 0;

    while app.wait() {
        if let Some(message) = r.recv() {
            match message {
                Message::Insert => {
                    let userdata = app_ui.get_edit_userdata();
                    manage_error_in_alert(
                        database.insert_userdata(&userdata.username, userdata.age),
                    );
                    s.send(Message::QueryAll)
                }
                Message::QueryAll => match database.get_all_userdata(limit, page * limit) {
                    Ok(users) => {
                        app_ui.load_query_data(&users);
                        app_ui.set_page(page as u64);
                    }
                    Err(error) => alert_default(error.to_string().as_str()),
                },
                Message::SelectUser => {
                    if let Some(userdata) = app_ui.get_select_userdata() {
                        app_ui.set_edit_userdata(&userdata)
                    }
                }
                Message::UpdateUser => {
                    if let Some(s_user) = app_ui.get_select_userdata() {
                        let j = app_ui.get_edit_userdata();

                        database
                            .update_userdata(s_user.id, &j.username, j.age)
                            .unwrap();
                        s.send(Message::QueryAll)
                    }
                }
                Message::DeleteUser => {
                    if let Some(s_user) = app_ui.get_select_userdata() {
                        database.delete_userdata(s_user.id).unwrap();
                        s.send(Message::QueryAll);
                    }
                }
                Message::SetRows => {
                    if let Some(rows) =
                        input_default("Set display rows:", limit.to_string().as_str())
                    {
                        if let Ok(rows) = rows.parse::<u64>() {
                            limit = rows
                        }
                    }
                    page = 0;
                    s.send(Message::QueryAll)
                }

                Message::Before => {
                    if page > 0 {
                        page = page - 1;
                        s.send(Message::QueryAll)
                    }
                }

                Message::Next => {
                    let count = database.get_count().unwrap();
                    if (page + 1) * limit < count {
                        page = page + 1;
                        s.send(Message::QueryAll)
                    }
                }

                Message::Refresh => {
                    page = 0;
                    s.send(Message::QueryAll);
                }

                Message::InsertMany => {
                    if let Some(count) = input_default("Insert count:", "1") {
                        if let Ok(count) = count.parse::<u64>() {
                            if count > 10000 {
                                alert_default("Count is over the limit 10000");
                            } else {
                                let userdata = app_ui.get_edit_userdata();
                                let _ = database.insert_many_userdata(
                                    count,
                                    &userdata.username,
                                    userdata.age,
                                );
                            }
                        }
                    }
                }

                Message::SetPage => {

                }
            }
        }
    }
}
# data file for the Fltk User Interface Designer (fluid)
version 1.0400
header_name {.h}
code_name {.cxx}
class UserInterface {open
} {
  Function {make_window()} {open
  } {
    Fl_Window {} {
      label {Sqlite 3 Sample} open
      xywh {364 207 786 409} type Double visible
    } {
      Fl_Input input_username {
        label {Username: }
        xywh {90 52 160 27}
      }
      Fl_Input input_age {
        label {Age: }
        xywh {90 88 160 27} type Float
      }
      Fl_Input input_id {
        label {ID:}
        xywh {90 18 160 27} deactivate
      }
      Fl_Button btn_insert_new {
        label {Insert New}
        xywh {25 129 100 31}
      }
      Fl_Button btn_many {
        label {Insert Many}
        comment BtnInsertMany
        xywh {25 169 100 31}
      }
      Fl_Button btn_insert_to_select {
        label {Update to select}
        xywh {135 130 115 30}
      }
      Fl_Button btn_delete_select {
        label {Delect select}
        xywh {135 170 115 30}
      }
      Fl_Button btn_set_rows {
        label {Set rows}
        xywh {392 18 100 30}
      }
      Fl_Button btn_refresh {
        label Refresh
        xywh {277 18 100 30}
      }
      Fl_Button btn_go_before {
        label Before
        xywh {280 305 60 30}
      }
      Fl_Button btn_go_next {
        label Next
        xywh {350 305 55 30}
      }
      Fl_Output op_page {
        label {Page: }
        xywh {670 307 50 28}
      }
      Fl_Browser sql_browser {selected
        xywh {277 63 445 225} type Multi
      }
    }
  }
}
[dependencies]
anyhow = "1.0.75"
fl2rust-macro = "0.5.17"
fltk = { version = "1.4.19", features = ["fltk-bundled"] }
rusqlite = { version = "0.30.0", features = ["bundled"] }
最后修改:2024 年 03 月 02 日
如果觉得我的文章对你有用,请随意赞赏