FLTK-Rs 2

news/2024/12/5 5:29:36/

Trees

树形结构,允许在树中显示项目,使用add方法发添加条目

use fltk::{prelude::*, *};
use fltk::enums::FrameType;fn main() {let a = celet mut win = window::Window::default().with_size(400, 300);let mut tree = tree::Tree::new(5,5,390,290,"");tree.set_root_label("Trceee_try");tree.add("Item 1");tree.add("Item 2");tree.add("Item 3");tree.add("Item 1/1");tree.add("Item 1/2");tree.add("Item 2/3");win.end();win.show();tree.set_callback(|tree| match tree.get_selected_items(){Some(s) => {for item in s{ println!("{} selected", tree.item_pathname(&item).unwrap());}}_ =>{},});a.run().unwrap();
}

我们的树组件初始化设定为仅单选。实际上我们可以通过一些设置设置样式与多选

// 允许多选
tree.set_select_mode(tree::TreeSelect::Multi);
// 实现或者虚线
tree.set_connector_style(tree::TreeConnectorStyle::Dotted);
// 线条颜色
tree.set_connector_color(Color::from_rgb(127,187,135));

Custom widgets

除了上述组件之外(我觉得够用了), fltk-rs还允许我们自定义组件。我们自定义的组件可以是完全自己写的~~(应该不会有人从零开写吧。。)~~, 或者是从前面的组件衍生出来的。我们定义组件结构体的时候要定义两个东西:衍生的组件,以及组件的数据的存储。

eg.

struct MyCustomButton {// 我们的组件inner: widget::Widget,// 我们的数据num_clicks: Rc<RefCell<i32>>,
}

我们这里存储数据使用的是Rc<RefCell<some>>>的结构。这个很重要, 因为我们在回调(callback)的时候我们需要移动这个数据到callback块中。然而,我们访问的时候却又要改变他,所以所有权一直在我们GUI调用线程中。所以使用RC<RefCell的方式定义保证数据的可用性。举个简单的例子:

use std::rc::Rc;
fn main() {let p = Rc::from(RefCell::from(10));// p,q指向同一块地址let q = p.clone();// 拿走了p的所有权let h = (move ||{*p.borrow_mut() = 20;});h();// 但是q仍能访问,而p也可以在其他块中被更改println!("{:?}",q);
}
RefCell { value: 20 }

所以我们的数据通过Rc 包裹 Refcell的方式去存储。

定义好结构体后,我们就需要定义方法了。其中最重要的方法就是初始化

struct MyCustomButton {inner: widget::Widget,num_clicks: Rc<RefCell<i32>>,
}impl MyCustomButton {// 我们的结构体// 我们定义的是一个圆形的按钮,r是半径pub fn new(radius: i32, label: &str) -> Self {// 我们的圆形站的尺寸的xy应该是直径缩影成2,最中间let mut inner = widget::Widget::default().with_size(radius * 2, radius * 2).with_label(label).center_of_parent();// 设定帧样式,后面我会介绍。其实就是一俺家样式,样式是库里的没啥问题inner.set_frame(enums::FrameType::OFlatBox);// 初始化我们的数据let num_clicks = 0;let num_clicks = Rc::from(RefCell::from(num_clicks));let clicks = num_clicks.clone();// 在按钮内部写字,协商按钮是干啥的。这个draw有点意思inner.draw(|i| { // we need a draw implementationdraw::draw_box(i.frame(), i.x(), i.y(), i.w(), i.h(), i.color());draw::set_draw_color(enums::Color::Black); // for the textdraw::set_font(enums::Font::Helvetica, 14);draw::draw_text2("Our button", i.x(), i.y(), i.w(), i.h(), i.align());});// 这个ev 我不太清楚干嘛用的,不过是判断点击用的我倒是清楚inner.handle(move |i, ev| match ev {enums::Event::Push => {// 与我上面演示的变化原理相同*clicks.borrow_mut() += 1;// 这个call_back应该是默认为空i.do_callback(); // do the callback which we'll set using set_callback().true}_ => false,});Self {inner,num_clicks,}}// get the times our button was clicked// 这个功能是鸡肋,应该,,pub fn num_clicks(&self) -> i32 {*self.num_clicks.borrow()}
}

最后我们想使用的话需要使用extren宏将其变成我们可以使用的组件:

widget_extends!(MyCustomButton, widget::Widget, inner);

运行效果:

fn main() {let app = app::App::default().with_scheme(app::Scheme::Gleam);app::background(255, 255, 255); // make the background whitelet mut wind = window::Window::new(100, 100, 400, 300, "Hello from rust");let mut btn = MyCustomButton::new(50, "Click");btn.set_color(enums::Color::Cyan);btn.set_callback(|_| println!("Clicked"));wind.end();wind.show();app.run().unwrap();// print the number our button was clicked on exitprintln!("Our button was clicked {} times", btn.num_clicks());
}
Clicked
Our button was clicked 1 times

在这里插入图片描述

至此,常用的组件就介绍完毕了

Dialogs

我之前以为对话框应该算是组件的一个部分,可是没想到官方的数把它单列出来了啊,看来还是有点东西的

对话框可以分为两种类型,原生的(操作系统自带)的文件对话框(对于我Windows用户就是win32对话框) 和FLTK自己的对话框。

关于本机的对话框倒是没什么好说的,直接调用即可:

let mut dialog = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseFile);
dialog.show();
// 返回文件的路径
println!("{:?}", dialog.filename());

这里的对话框有六个接口可供选择,单选文件,文件夹;多选文件,文件夹; 保存文件,文件夹路径。我们还可以加文件类型过滤:

dialog.set_filter("*.{txt,rs,toml,py}");

总之还是很简单的,重点是FLTK自带的对话框:

FLTK提供了几个对话框方便我们构建应用:

  • Help 这个对话框可以显示html文档,当然也可以我们自己输入显示的文字

    let mut help = dialog::HelpDialog::new(100, 100, 400, 300);
    help.set_value("<h2>Hello world</h2>"); // this takes html
    help.show();
    // 在另一个窗口展示的时候挂起主应用
    while help.shown() {app::wait();
    }
    

    在这里插入图片描述

  • Message:

    //dialog::message(坐标x,坐标y,"你要写的话”);, 效果和下面一样,只不过自定义了位置。不定义的话就是center默认
    dialog::message_default("This is a Message\nA important message");
    

    在这里插入图片描述

  • Choice:选择对话框,不过只能由三个选项

    // 设置弹窗标题
    dialog::message_title_default("Save or Not");
    // 选项的显示是从左到右的0,1,2
    let choice = dialog::choice2_default("Would you like to save", "No", "Yes", "Cancel");
    // 这里的choice返回的是Some类型
    println!("{:?}", choice);
    

    在这里插入图片描述

​ cancle 2, Yes 1, No 0

  • Input/ Password:

    dialog::message_title_default("Input name");
    let choice = dialog::input_default("Pleasr input your name", "None");
    // 返回值依然是Some
    println!("{:?}", choice);
    //password就是input的铭文编程不可见的密文小点点。
    

    在这里插入图片描述

Custom Dialog:

这些对话框并不是不好看,但是怎么说呢,上个时代的风格确实太浓了点,而且没办法更自由地定义一些东西。所以我们需要自定义对话框。对话框,本质是新开的窗口,话句话说我们定义窗口即可。

// 官网的小李子
use fltk::{app, button,enums::{Color, Font, FrameType},frame, group, input,prelude::*,window,
};// 设置button的样式
fn style_button(btn: &mut button::Button) {btn.set_color(Color::Cyan);btn.set_frame(FrameType::RFlatBox);btn.clear_visible_focus();
}pub fn show_dialog() -> MyDialog {MyDialog::default()
}pub struct MyDialog {inp: input::Input,
}impl MyDialog {pub fn default() -> Self {let mut win = window::Window::default().with_size(400, 100).with_label("My Dialog");// 这里原来是由用到布局工具的,但是我把它改了方便大家看win.set_color(Color::from_rgb(240, 240, 240));let mut inp = input::Input::new(150,35, 100, 30,"Input name");inp.set_frame(FrameType::FlatBox);let mut ok = button::Button::new(300,35,80, 30,"Ok");style_button(&mut ok);win.end();// 只允许运行当前窗体达到弹窗的效果,不允许窗体关闭win.make_modal(true);win.show();ok.set_callback({let mut win = win.clone();move |_| {// 窗体关闭,由于我们要让主appwait, 所以我们下面海涌到了win,所以这里用的clone方法win.hide();}});while win.shown() {app::wait();}Self { inp }}pub fn value(&self) -> String {self.inp.value()}
}fn main() {let a = app::App::default();app::set_font(Font::Times);let mut win = window::Window::default().with_size(600, 400);win.set_color(Color::from_rgb(240, 240, 240));let mut btn = button::Button::default().with_size(80, 30).with_label("Click").center_of_parent();style_button(&mut btn);// 这里没想到这个frame布局还可以这么用,挺好玩的let mut frame = frame::Frame::new(btn.x() - 40, btn.y() - 100, btn.w() + 80, 30, None);frame.set_frame(FrameType::BorderBox);frame.set_color(Color::Red.inactive());win.end();win.show();btn.set_callback(move |_| {let d = show_dialog();// 同步更新frame.set_label(&d.value());});a.run().unwrap();
}

在这里插入图片描述

Pictures

我们GUI无论样式如何都是要使用图片来美化我们的应用的,FLTK支持一堆图片使用的,请参考官网支持的图片种类:https://fltk-rs.github.io/fltk-book/Images.html

fn main() {let app = app::App::default().with_scheme(app::Scheme::Gleam);let mut wind = window::Window::new(100, 100, 400, 300, "Hello from rust");let mut frame = frame::Frame::new(20,20,90,160,"");let mut image = image::JpegImage::load("。。。1.jpg").unwrap();// 强转大小image.scale(90,160,true,true);frame.set_image(Some(image));wind.make_resizable(true);wind.end();wind.show();// 当图片被点击的时候触发frame.handle(|frame,ev|{match ev{enums::Event::Push => {// 与我上面演示的变化原理相同println!("Clicked");true}_ => false,}});app.run().unwrap();
}

简单代码意思一下。此外除了set_picture, 我们还可以使用draw方法,这样在大部分组件中都可以使用图片了

use fltk::{prelude::*, *};
use fltk::enums::FrameType;fn main() {let app = app::App::default().with_scheme(app::Scheme::Gleam);let mut wind = window::Window::new(100, 100, 400, 300, "Hello from rust");let mut button = button::Button::new(10,10,200,200,"");let mut image = image::JpegImage::load("C:/Users/40629/Desktop/image/1.jpg").unwrap();button.draw(move |b|{image.scale(b.w(),b.h(),true,true);image.draw(b.x(),b.y(),b.w(),b.h());});wind.make_resizable(true);wind.end();wind.show();button.set_callback(|button|{println!("clicked");});app.run().unwrap();
}

Events

我们前面所接触的大部分不见为我们提供了callback方法进行回调,但是fltk给了我们更多的操作空间,比如我之前使用handle.这一节会讲解这些东西的用法:

  • callback,前面已经讲过很多了,我觉得没必要再说了。callback可以接受闭包,也可以接收函数。闭包只是因为更方便

  • handle: handle 方法接受一个参数为 Event 的闭包,并为已处理的事件返回一个 bool。bool 让 FLTK 知道事件是否被处理。这也是为什么我前面的代码都要返回一个bool的原因。eg.

    frame.handle(|frame,ev|{match ev{// Push单机,此外还有一些方法enums::Event::Push => {// 与我上面演示的变化原理相同println!("Clicked");true}_ => false,}});
    
  • emit: 使用sender/ receiver的消息使用消费者模型,我们在菜单组件中也有介绍过哦:

    fn main() {let app = app::App::default();let mut my_window = window::Window::new(100, 100, 400, 300, "My Window");let mut but = button::Button::new(160, 200, 80, 40, "Click me!");my_window.end();my_window.show();// 这里是用app_cahhel定义,而不是我们生产者消费者模型的channel定义// 当然,这里用生产者消费者模型也完全没问题let (s, r) = app::channel();but.emit(s, true);// 循环监听while app.wait() {// 收到消息(没被阻塞)就完成下面的操作if let Some(msg) = r.recv() {match msg {true => println!("Clicked"),false => (), // Here we basically do nothing}}}
    }
    
  • 自定义事件:讲道理我觉得已有的29个事件够用了,我反正是没啥遗憾啦,如果想看自定义事件参见:https://fltk-rs.github.io/fltk-book/Events.html


http://www.ppmy.cn/news/496342.html

相关文章

Rock 5 RK3588 SBC初次摸索

RADXA Rock 5&#xff08;基于RK3588&#xff09;的初次摸索 硬件平台简述系统安装选择几个系统的介绍系统的安装方式 配件方面 硬件平台简述 RK3588是瑞芯微发布的一款8核&#xff08;4核大4核小&#xff09;ARM架构处理器&#xff0c;许多家都有这个芯片的开发板。 这里使用…

连接器 SM05B-PASS-TBT R/A 5POS 2MM SMD

CONN HEADER SMD R/A 5POS 2MM&#xff0c;连接器 SM05B-PASS-TBT。 品牌&#xff1a;JST 2mm间距PA系列由以下连接器组成: -PA连接器(线对板压接型) -PAF连接器(线对板绝缘置换连接(IDC)型) -PAL连接器(线对线压接式) 这些特征使得形状复杂的haress首次成为具有安全锁定机构…

[单片机][KEIL5] 参数的意义和使用 L #L @L K #K $, #, %, @, ~, ^

使用键序列从Vision传递参数给外部用户程序。 键序列是键码和文件码的组合。 键序列可以使用&#xff0c;例如&#xff0c;在对话框工具&#xff0c;SVCS&#xff0c;选项的目标用户&#xff0c;或从命令行。 以下规则适用: 当从命令行使用时&#xff0c;某些关键代码必须重复…

敏源传感 MTS01 IIC CRC校验程序(x8 + x5 + x4 + 1)

敏源传感 MTS01 IIC CRC校验程序&#xff1a; /* *Name: CRC-8 x8x5x41 * Poly: 0x31 * Init: 0xFF * Refin: False * Refout: False …

C51简介及Keil的使用

前言 此文档主要是针对有一定C/C编程基础&#xff0c;并打算用Keil从事C51开发的开发人员。C51涉及的知识比较多&#xff0c;但是入门基本的开发&#xff0c;还是容易的。 C51简介 1. C51概念 C51继承于C语言&#xff0c;主要运行于51内核的单片机平台。单片机&#xff0c;…

C语言进阶教程(再论struct,union,enum)

文章目录 前言一、struct&#xff0c;union&#xff0c;enum类型大小计算二、应用场景分析总结 前言 本篇文章我们再来讨论struct&#xff0c;union&#xff0c;enum这三大结构。这三大结构很容易让人晕头转向&#xff0c;所以现在我们再来分析一下吧。 一、struct&#xff0…

gRPC教程与应用

gRPC是是谷歌一个开源的跨语言的RPC框架&#xff0c;面向移动和 HTTP/2 设计。 grpc中文网 在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法&#xff0c;使得您能够更容易地创建分布式应用和服务。 gRPC 也是基于以下理念&#xff1…

Java 从入门到精通(续集17)——数据库编程

Java 从入门到精通&#xff08;续集17&#xff09;——数据库编程 Java 中的数据库编程是指使用 Java 提供的 JDBC API 和各种数据库驱动程序来实现与数据库之间的数据交互。Java 中主要使用的数据库驱动程序包括 MySQL、Oracle、SQL Server 等。 一、JDBC API Java 中的 JDBC…