0061-Tui-迷你图示例

环境

  • Time 2022-08-16
  • Rust 1.63.0
  • Tui 0.18.0

前言

说明

参考:https://github.com/fdehau/tui-rs/blob/master/examples/sparkline.rs

目标

使用 tui-rs 显示迷你图。

生成数据

#[derive(Clone)]
pub struct RandomSignal {
    distribution: Uniform<u64>,
    rng: ThreadRng,
}

impl RandomSignal {
    pub fn new(lower: u64, upper: u64) -> RandomSignal {
        RandomSignal {
            distribution: Uniform::new(lower, upper),
            rng: rand::thread_rng(),
        }
    }
}

impl Iterator for RandomSignal {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        Some(self.distribution.sample(&mut self.rng))
    }
}

定义应用

struct App {
    signal: RandomSignal,
    data1: Vec<u64>,
    data2: Vec<u64>,
    data3: Vec<u64>,
}

impl App {
    fn new() -> App {
        let mut signal = RandomSignal::new(0, 100);
        let data1 = signal.by_ref().take(200).collect::<Vec<u64>>();
        let data2 = signal.by_ref().take(200).collect::<Vec<u64>>();
        let data3 = signal.by_ref().take(200).collect::<Vec<u64>>();
        App {
            signal,
            data1,
            data2,
            data3,
        }
    }

    fn on_tick(&mut self) {
        let value = self.signal.next().unwrap();
        self.data1.pop();
        self.data1.insert(0, value);
        let value = self.signal.next().unwrap();
        self.data2.pop();
        self.data2.insert(0, value);
        let value = self.signal.next().unwrap();
        self.data3.pop();
        self.data3.insert(0, value);
    }
}

ui

fn ui<B: Backend>(frame: &mut Frame<B>, app: &mut App) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(2)
        .constraints([
            Constraint::Length(3),
            Constraint::Length(3),
            Constraint::Length(7),
            Constraint::Min(0),
        ])
        .split(frame.size());
    let sparkline = widgets::Sparkline::default()
        .block(
            Block::default()
                .title("温度")
                .borders(Borders::LEFT | Borders::RIGHT),
        )
        .data(&app.data1)
        .style(Style::default().fg(Color::Yellow));
    frame.render_widget(sparkline, chunks[0]);
    let sparkline = widgets::Sparkline::default()
        .block(
            Block::default()
                .title("湿度")
                .borders(Borders::LEFT | Borders::RIGHT),
        )
        .data(&app.data2)
        .style(Style::default().bg(Color::Green));
    frame.render_widget(sparkline, chunks[1]);
    // Multiline
    let sparkline = widgets::Sparkline::default()
        .block(
            Block::default()
                .title("降水")
                .borders(Borders::LEFT | Borders::RIGHT),
        )
        .data(&app.data3)
        .style(Style::default().fg(Color::Red));
    frame.render_widget(sparkline, chunks[2]);
}

效果展示

迷你图

总结

使用 tui-rs 渲染迷你图。

附录

源码

use anyhow::{Context, Result};
use crossterm::{event, terminal, ExecutableCommand};
use layout::{Constraint, Direction, Layout};
use rand::distributions::Uniform;
use rand::prelude::Distribution;
use rand::rngs::ThreadRng;
use style::{Color, Style};
use tui::backend::{Backend, CrosstermBackend};
use tui::{layout, style, widgets, Frame, Terminal};
use widgets::{Block, Borders};

pub fn main() -> Result<()> {
    terminal::enable_raw_mode()?;
    let mut backend = CrosstermBackend::new(std::io::stdout());
    backend
        .execute(terminal::EnterAlternateScreen)?
        .execute(terminal::Clear(terminal::ClearType::All))?
        .hide_cursor()?;
    let mut terminal = tui::Terminal::new(backend)?;
    run(&mut terminal)?;
    terminal::disable_raw_mode()?;
    terminal
        .backend_mut()
        .execute(terminal::Clear(terminal::ClearType::All))?
        .execute(terminal::LeaveAlternateScreen)?
        .show_cursor()
        .context("重置控制台失败")
}

fn run<B: Backend>(terminal: &mut Terminal<B>) -> Result<()> {
    let timeout = std::time::Duration::from_millis(500);
    let mut app = App::new();
    loop {
        terminal.draw(|frame| ui(frame, &mut app))?;

        if event::poll(timeout)? {
            if let event::Event::Key(key) = event::read()? {
                use event::KeyCode::{Char, Esc};
                match key.code {
                    Char('q') | Char('Q') | Esc => return Ok(()),
                    _ => {}
                }
            }
        }
        app.on_tick();
    }
}

#[derive(Clone)]
pub struct RandomSignal {
    distribution: Uniform<u64>,
    rng: ThreadRng,
}

impl RandomSignal {
    pub fn new(lower: u64, upper: u64) -> RandomSignal {
        RandomSignal {
            distribution: Uniform::new(lower, upper),
            rng: rand::thread_rng(),
        }
    }
}

impl Iterator for RandomSignal {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        Some(self.distribution.sample(&mut self.rng))
    }
}

struct App {
    signal: RandomSignal,
    data1: Vec<u64>,
    data2: Vec<u64>,
    data3: Vec<u64>,
}

impl App {
    fn new() -> App {
        let mut signal = RandomSignal::new(0, 100);
        let data1 = signal.by_ref().take(200).collect::<Vec<u64>>();
        let data2 = signal.by_ref().take(200).collect::<Vec<u64>>();
        let data3 = signal.by_ref().take(200).collect::<Vec<u64>>();
        App {
            signal,
            data1,
            data2,
            data3,
        }
    }

    fn on_tick(&mut self) {
        let value = self.signal.next().unwrap();
        self.data1.pop();
        self.data1.insert(0, value);
        let value = self.signal.next().unwrap();
        self.data2.pop();
        self.data2.insert(0, value);
        let value = self.signal.next().unwrap();
        self.data3.pop();
        self.data3.insert(0, value);
    }
}

fn ui<B: Backend>(frame: &mut Frame<B>, app: &mut App) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(2)
        .constraints([
            Constraint::Length(3),
            Constraint::Length(3),
            Constraint::Length(7),
            Constraint::Min(0),
        ])
        .split(frame.size());
    let sparkline = widgets::Sparkline::default()
        .block(
            Block::default()
                .title("温度")
                .borders(Borders::LEFT | Borders::RIGHT),
        )
        .data(&app.data1)
        .style(Style::default().fg(Color::Yellow));
    frame.render_widget(sparkline, chunks[0]);
    let sparkline = widgets::Sparkline::default()
        .block(
            Block::default()
                .title("湿度")
                .borders(Borders::LEFT | Borders::RIGHT),
        )
        .data(&app.data2)
        .style(Style::default().bg(Color::Green));
    frame.render_widget(sparkline, chunks[1]);
    // Multiline
    let sparkline = widgets::Sparkline::default()
        .block(
            Block::default()
                .title("降水")
                .borders(Borders::LEFT | Borders::RIGHT),
        )
        .data(&app.data3)
        .style(Style::default().fg(Color::Red));
    frame.render_widget(sparkline, chunks[2]);
}
posted @   jiangbo4444  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2020-09-30 【JavaScript】展开语法
2020-09-30 【JavaScript】剩余参数
2020-09-30 【JavaScript】默认参数
2020-09-30 【JavaScript】import
2020-09-30 【JavaScript】debugger
2020-09-30 【JavaScript】for await...of
点击右上角即可分享
微信分享提示