首页 > 其他分享 >0064-Tui-图表示例

0064-Tui-图表示例

时间:2022-09-30 21:25:07浏览次数:72  
标签:Style Span 示例 default text self Tui 0064 let

环境

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

前言

说明

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

目标

使用 tui-rs 显示图表。

常量数据

const DATA: [(f64, f64); 5] = [(0.0, 0.0), (1.0, 1.0), (2.0, 2.0), (3.0, 3.0), (4.0, 4.0)];
const DATA2: [(f64, f64); 7] = [
    (0.0, 0.0),
    (10.0, 1.0),
    (20.0, 0.5),
    (30.0, 1.5),
    (40.0, 1.0),
    (50.0, 2.5),
    (60.0, 3.0),
];

定义应用


#[derive(Clone)]
pub struct SinSignal {
    x: f64,
    interval: f64,
    period: f64,
    scale: f64,
}

impl SinSignal {
    pub fn new(interval: f64, period: f64, scale: f64) -> SinSignal {
        SinSignal {
            x: 0.0,
            interval,
            period,
            scale,
        }
    }
}

impl Iterator for SinSignal {
    type Item = (f64, f64);
    fn next(&mut self) -> Option<Self::Item> {
        let point = (self.x, (self.x * 1.0 / self.period).sin() * self.scale);
        self.x += self.interval;
        Some(point)
    }
}

struct App {
    signal1: SinSignal,
    data1: Vec<(f64, f64)>,
    signal2: SinSignal,
    data2: Vec<(f64, f64)>,
    window: [f64; 2],
}

impl App {
    fn new() -> App {
        let mut signal1 = SinSignal::new(0.2, 3.0, 18.0);
        let mut signal2 = SinSignal::new(0.1, 2.0, 10.0);
        let data1 = signal1.by_ref().take(200).collect::<Vec<(f64, f64)>>();
        let data2 = signal2.by_ref().take(200).collect::<Vec<(f64, f64)>>();
        App {
            signal1,
            data1,
            signal2,
            data2,
            window: [0.0, 20.0],
        }
    }

    fn on_tick(&mut self) {
        for _ in 0..5 {
            self.data1.remove(0);
        }
        self.data1.extend(self.signal1.by_ref().take(5));
        for _ in 0..10 {
            self.data2.remove(0);
        }
        self.data2.extend(self.signal2.by_ref().take(10));
        self.window[0] += 1.0;
        self.window[1] += 1.0;
    }
}

布局

fn ui<B: Backend>(frame: &mut Frame<B>, app: &mut App) {
    let chunks = Layout::default()
        .direction(layout::Direction::Vertical)
        .constraints([
            Constraint::Ratio(1, 3),
            Constraint::Ratio(1, 3),
            Constraint::Ratio(1, 3),
        ])
        .split(frame.size());

    render_top(frame, chunks[0], app);
    render_mid(frame, chunks[1]);
    render_bot(frame, chunks[2]);
}

render_top

fn render_top<B: Backend>(frame: &mut Frame<B>, area: layout::Rect, app: &App) {
    let x_labels = vec![
        text::Span::styled(
            format!("{}", app.window[0]),
            Style::default().add_modifier(Modifier::BOLD),
        ),
        text::Span::raw(format!("{}", (app.window[0] + app.window[1]) / 2.0)),
        text::Span::styled(
            format!("{}", app.window[1]),
            Style::default().add_modifier(Modifier::BOLD),
        ),
    ];
    let datasets = vec![
        Dataset::default()
            .name("data2")
            .marker(symbols::Marker::Dot)
            .style(Style::default().fg(Color::Cyan))
            .data(&app.data1),
        Dataset::default()
            .name("data3")
            .marker(symbols::Marker::Braille)
            .style(Style::default().fg(Color::Yellow))
            .data(&app.data2),
    ];

    let chart = Chart::new(datasets)
        .block(
            Block::default()
                .title(text::Span::styled(
                    "Chart 1",
                    Style::default()
                        .fg(Color::Cyan)
                        .add_modifier(Modifier::BOLD),
                ))
                .borders(Borders::ALL),
        )
        .x_axis(
            Axis::default()
                .title("X Axis")
                .style(Style::default().fg(Color::Gray))
                .labels(x_labels)
                .bounds(app.window),
        )
        .y_axis(
            Axis::default()
                .title("Y Axis")
                .style(Style::default().fg(Color::Gray))
                .labels(vec![
                    text::Span::styled("-20", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("0"),
                    text::Span::styled("20", Style::default().add_modifier(Modifier::BOLD)),
                ])
                .bounds([-20.0, 20.0]),
        );
    frame.render_widget(chart, area);
}

render_mid

fn render_mid<B: Backend>(frame: &mut Frame<B>, area: layout::Rect) {
    let datasets = vec![Dataset::default()
        .name("data")
        .marker(symbols::Marker::Braille)
        .style(Style::default().fg(Color::Yellow))
        .graph_type(GraphType::Line)
        .data(&DATA)];
    let chart = Chart::new(datasets)
        .block(
            Block::default()
                .title(text::Span::styled(
                    "Chart 2",
                    Style::default()
                        .fg(Color::Cyan)
                        .add_modifier(Modifier::BOLD),
                ))
                .borders(Borders::ALL),
        )
        .x_axis(
            Axis::default()
                .title("X Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 5.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("2.5"),
                    text::Span::styled("5.0", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        )
        .y_axis(
            Axis::default()
                .title("Y Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 5.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("2.5"),
                    text::Span::styled("5.0", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        );
    frame.render_widget(chart, area);
}

render_bot

fn render_bot<B: Backend>(frame: &mut Frame<B>, area: layout::Rect) {
    let datasets = vec![Dataset::default()
        .name("data")
        .marker(symbols::Marker::Braille)
        .style(Style::default().fg(Color::Yellow))
        .graph_type(GraphType::Line)
        .data(&DATA2)];
    let chart = Chart::new(datasets)
        .block(
            Block::default()
                .title(text::Span::styled(
                    "Chart 3",
                    Style::default()
                        .fg(Color::Cyan)
                        .add_modifier(Modifier::BOLD),
                ))
                .borders(Borders::ALL),
        )
        .x_axis(
            Axis::default()
                .title("X Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 50.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("25"),
                    text::Span::styled("50", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        )
        .y_axis(
            Axis::default()
                .title("Y Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 5.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("2.5"),
                    text::Span::styled("5", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        );
    frame.render_widget(chart, area);
}

效果展示

图表

总结

使用 tui-rs 渲染图表。

附录

源码

use anyhow::{Context, Result};
use crossterm::{event, terminal, ExecutableCommand};
use layout::{Constraint, Layout};
use tui::backend::{Backend, CrosstermBackend};
use tui::style::{Color, Modifier, Style};
use tui::{layout, symbols, text, widgets, Frame, Terminal};
use widgets::{Axis, Block, Borders, Chart, Dataset, GraphType};

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("重置控制台失败")
}

const DATA: [(f64, f64); 5] = [(0.0, 0.0), (1.0, 1.0), (2.0, 2.0), (3.0, 3.0), (4.0, 4.0)];
const DATA2: [(f64, f64); 7] = [
    (0.0, 0.0),
    (10.0, 1.0),
    (20.0, 0.5),
    (30.0, 1.5),
    (40.0, 1.0),
    (50.0, 2.5),
    (60.0, 3.0),
];

#[derive(Clone)]
pub struct SinSignal {
    x: f64,
    interval: f64,
    period: f64,
    scale: f64,
}

impl SinSignal {
    pub fn new(interval: f64, period: f64, scale: f64) -> SinSignal {
        SinSignal {
            x: 0.0,
            interval,
            period,
            scale,
        }
    }
}

impl Iterator for SinSignal {
    type Item = (f64, f64);
    fn next(&mut self) -> Option<Self::Item> {
        let point = (self.x, (self.x * 1.0 / self.period).sin() * self.scale);
        self.x += self.interval;
        Some(point)
    }
}

struct App {
    signal1: SinSignal,
    data1: Vec<(f64, f64)>,
    signal2: SinSignal,
    data2: Vec<(f64, f64)>,
    window: [f64; 2],
}

impl App {
    fn new() -> App {
        let mut signal1 = SinSignal::new(0.2, 3.0, 18.0);
        let mut signal2 = SinSignal::new(0.1, 2.0, 10.0);
        let data1 = signal1.by_ref().take(200).collect::<Vec<(f64, f64)>>();
        let data2 = signal2.by_ref().take(200).collect::<Vec<(f64, f64)>>();
        App {
            signal1,
            data1,
            signal2,
            data2,
            window: [0.0, 20.0],
        }
    }

    fn on_tick(&mut self) {
        for _ in 0..5 {
            self.data1.remove(0);
        }
        self.data1.extend(self.signal1.by_ref().take(5));
        for _ in 0..10 {
            self.data2.remove(0);
        }
        self.data2.extend(self.signal2.by_ref().take(10));
        self.window[0] += 1.0;
        self.window[1] += 1.0;
    }
}

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();
    }
}

fn ui<B: Backend>(frame: &mut Frame<B>, app: &mut App) {
    let chunks = Layout::default()
        .direction(layout::Direction::Vertical)
        .constraints([
            Constraint::Ratio(1, 3),
            Constraint::Ratio(1, 3),
            Constraint::Ratio(1, 3),
        ])
        .split(frame.size());

    render_top(frame, chunks[0], app);
    render_mid(frame, chunks[1]);
    render_bot(frame, chunks[2]);
}

fn render_top<B: Backend>(frame: &mut Frame<B>, area: layout::Rect, app: &App) {
    let x_labels = vec![
        text::Span::styled(
            format!("{}", app.window[0]),
            Style::default().add_modifier(Modifier::BOLD),
        ),
        text::Span::raw(format!("{}", (app.window[0] + app.window[1]) / 2.0)),
        text::Span::styled(
            format!("{}", app.window[1]),
            Style::default().add_modifier(Modifier::BOLD),
        ),
    ];
    let datasets = vec![
        Dataset::default()
            .name("data2")
            .marker(symbols::Marker::Dot)
            .style(Style::default().fg(Color::Cyan))
            .data(&app.data1),
        Dataset::default()
            .name("data3")
            .marker(symbols::Marker::Braille)
            .style(Style::default().fg(Color::Yellow))
            .data(&app.data2),
    ];

    let chart = Chart::new(datasets)
        .block(
            Block::default()
                .title(text::Span::styled(
                    "Chart 1",
                    Style::default()
                        .fg(Color::Cyan)
                        .add_modifier(Modifier::BOLD),
                ))
                .borders(Borders::ALL),
        )
        .x_axis(
            Axis::default()
                .title("X Axis")
                .style(Style::default().fg(Color::Gray))
                .labels(x_labels)
                .bounds(app.window),
        )
        .y_axis(
            Axis::default()
                .title("Y Axis")
                .style(Style::default().fg(Color::Gray))
                .labels(vec![
                    text::Span::styled("-20", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("0"),
                    text::Span::styled("20", Style::default().add_modifier(Modifier::BOLD)),
                ])
                .bounds([-20.0, 20.0]),
        );
    frame.render_widget(chart, area);
}

fn render_mid<B: Backend>(frame: &mut Frame<B>, area: layout::Rect) {
    let datasets = vec![Dataset::default()
        .name("data")
        .marker(symbols::Marker::Braille)
        .style(Style::default().fg(Color::Yellow))
        .graph_type(GraphType::Line)
        .data(&DATA)];
    let chart = Chart::new(datasets)
        .block(
            Block::default()
                .title(text::Span::styled(
                    "Chart 2",
                    Style::default()
                        .fg(Color::Cyan)
                        .add_modifier(Modifier::BOLD),
                ))
                .borders(Borders::ALL),
        )
        .x_axis(
            Axis::default()
                .title("X Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 5.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("2.5"),
                    text::Span::styled("5.0", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        )
        .y_axis(
            Axis::default()
                .title("Y Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 5.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("2.5"),
                    text::Span::styled("5.0", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        );
    frame.render_widget(chart, area);
}

fn render_bot<B: Backend>(frame: &mut Frame<B>, area: layout::Rect) {
    let datasets = vec![Dataset::default()
        .name("data")
        .marker(symbols::Marker::Braille)
        .style(Style::default().fg(Color::Yellow))
        .graph_type(GraphType::Line)
        .data(&DATA2)];
    let chart = Chart::new(datasets)
        .block(
            Block::default()
                .title(text::Span::styled(
                    "Chart 3",
                    Style::default()
                        .fg(Color::Cyan)
                        .add_modifier(Modifier::BOLD),
                ))
                .borders(Borders::ALL),
        )
        .x_axis(
            Axis::default()
                .title("X Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 50.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("25"),
                    text::Span::styled("50", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        )
        .y_axis(
            Axis::default()
                .title("Y Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds([0.0, 5.0])
                .labels(vec![
                    text::Span::styled("0", Style::default().add_modifier(Modifier::BOLD)),
                    text::Span::raw("2.5"),
                    text::Span::styled("5", Style::default().add_modifier(Modifier::BOLD)),
                ]),
        );
    frame.render_widget(chart, area);
}

标签:Style,Span,示例,default,text,self,Tui,0064,let
From: https://www.cnblogs.com/jiangbo4444/p/16746260.html

相关文章

  • 0065-Tui-Canvas 示例
    环境Time2022-08-17Rust1.63.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/canvas.rs目标使用tui-rs显示Canvas。定义......
  • 0066-Tui-自定义组件
    环境Time2022-08-17Rust1.63.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/custom_widget.rs目标使用tui-rs自定义一个......
  • 0067-Tui-panic 处理
    环境Time2022-08-17Rust1.63.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/panic.rs目标使用tui-rs定义一个panichook......
  • 0068-Tui-用户输入
    环境Time2022-08-18Rust1.63.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/user_input.rs目标使用tui-rs来处理用户的输......
  • 0062-Tui-表格示例
    环境Time2022-08-16Rust1.63.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/table.rs目标使用tui-rs显示表格。定义应用......
  • 0063-Tui-页签示例
    环境Time2022-08-16Rust1.63.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/tabs.rs目标使用tui-rs显示页签。定义应用......
  • 0058-Tui-段落示例
    环境Time2022-08-12Rust1.63.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/paragraph.rs目标使用tui-rs显示段落。定义......
  • 0055-Tui-条形图示例
    环境Time2022-08-09Rust1.62.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/barchart.rs目标使用tui-rs显示条形图。第......
  • 0056-Tui-简单进度条
    环境Time2022-08-11Rust1.62.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/gauge.rs目标使用tui-rs显示进度条。定义数......
  • 0057-Tui-进度条示例
    环境Time2022-08-11Rust1.62.0Tui0.18.0前言说明参考:https://github.com/fdehau/tui-rs/blob/master/examples/gauge.rs目标使用tui-rs显示进度条。render......