pub struct Chart { /* private fields */ }Expand description
Datasets on two axes, laid out as ratatui’s Chart, so a ratatui app
ports with the same look.
- The x axis’s labels take the bottom row and its line (
─) the row above; the y axis’s labels take the left (at most a third of the width) and its line (│) the column after, meeting at└. An axis draws its line only when it hasLabels. - The datasets are drawn in order on a
Canvasfilling the rest, each with its marker. Datasets with the same marker share the dots of a cell, which takes the later one’s style;Layering::PerDatasetdraws each over the ones before. A line leaving the bounds is clipped at them (OutOfBounds). - The y axis’s title sits on the plot’s top row, from its left; the x axis’s at the right end of the plot’s bottom row.
- The legend is a box listing the named datasets, each in its style, in
a corner of the plot (
LegendPosition::TopRightby default). It hides itself when it would take more than a share of the plot: a quarter of the width and a quarter of the height by default. Its box is in the chart’sstyle, or else the x axis’s, unlesslegend_stylesays otherwise, and it clears the plot under it unlesslegend_clearis false. - It fills the height it is given, as ratatui’s fills its area: a
Layoutregion, aPanelwith a height, an intuiTUIve node throughrenderable(..).heightfixes it instead; given no height, it is 15 rows. It is as wide as the width it is given (orwidth). As in ratatui, a short chart gives up the x axis line, then the x labels; a narrow one cuts the y labels to a third of the width; the titles and the legend show only when the plot has room for them.
use rich::Console;
use rich_ext::chart::{Axis, Chart, Dataset, GraphType, Labels, Marker};
let console = Console::builder().width(24).color_system(None).build();
let cpu = Dataset::new([(0.0, 0.0), (5.0, 4.0), (10.0, 1.0)])
.name("cpu")
.graph_type(GraphType::Line)
.marker(Marker::Ascii('*'));
let chart = Chart::new(vec![cpu])
.x_axis(Axis::default().bounds(0.0, 10.0).title("t").labels(Labels::Auto(3)))
.y_axis(Axis::default().bounds(0.0, 4.0).title("%").labels(Labels::Auto(3)))
.hidden_legend_fraction(0.5, 0.5)
.height(8);
assert_eq!(
console.render_export(&chart),
concat!(
"4│% *** ┌───┐\n",
" │ ** **** │cpu│\n",
" │ ** *└───┘\n",
"2│ ** **\n",
" │ ** \n",
"0│* t\n",
" └──────────────────────\n",
" 0 5 10\n",
)
);Implementations§
Source§impl Chart
impl Chart
Sourcepub fn stacked(self, stacked: bool) -> Self
pub fn stacked(self, stacked: bool) -> Self
Stack the GraphType::Area datasets (default off): each area’s
values are drawn on top of the areas before it, so the top edge is
their running total and each band is one dataset’s share. The axes
fit the totals (a linear y axis without bounds runs from the fill
level), and every stacked area fills down to the first
one’s fill_to level (0 by default), the top layer
drawn first, so each band shows in its own style. Datasets that
share x values stack exactly; between another area’s points the
area below is interpolated along its line. Other datasets are drawn
as they are, over the areas. The legend and the accessible text
keep each dataset’s own values.
use rich::Console;
use rich_ext::chart::{Axis, Chart, Dataset, GraphType, LegendPosition, Marker};
let console = Console::builder().width(8).color_system(None).build();
let area = |points: [(f64, f64); 2], c: char| {
Dataset::new(points).graph_type(GraphType::Area).marker(Marker::Ascii(c))
};
let chart = Chart::new(vec![
area([(0.0, 1.0), (7.0, 1.0)], '#'),
area([(0.0, 1.0), (7.0, 2.0)], '+'),
])
.x_axis(Axis::default().bounds(0.0, 7.0))
.y_axis(Axis::default().bounds(0.0, 3.0))
.legend(LegendPosition::None)
.stacked(true)
.width(8)
.height(4);
assert_eq!(
console.render_export(&chart),
concat!(
" ++++\n",
"++++++++\n",
"########\n",
"########\n",
)
);Source§impl Chart
impl Chart
Sourcepub fn new(datasets: impl IntoIterator<Item = Dataset>) -> Self
pub fn new(datasets: impl IntoIterator<Item = Dataset>) -> Self
A chart of datasets, drawn in order.
Sourcepub fn legend(self, position: LegendPosition) -> Self
pub fn legend(self, position: LegendPosition) -> Self
Where the legend goes (default LegendPosition::TopRight).
The largest share of the plot’s width and height the legend may
take before it hides (default a quarter of each). Each is clamped
to 0..=1; NaN keeps the default.
Sourcepub fn style(self, style: impl Into<String>) -> Self
pub fn style(self, style: impl Into<String>) -> Self
The chart’s own style, as ratatui’s Chart::style: a theme key or a
definition put under every cell of the chart (a background fills
it), and the style of the legend’s box unless
legend_style gives one. Without it the chart
has no style of its own and the legend’s box takes the x axis’s.
"none" is the terminal’s own colours, so the legend’s box is
unstyled too: legend_unstyled says the
same by name.
use rich::Console;
use rich_ext::chart::{Axis, Chart, Dataset, Marker};
let console = Console::builder().width(6).force_terminal(true).build();
let chart = Chart::new(vec![Dataset::new([]).name("a")])
.x_axis(Axis::default().bounds(0.0, 1.0))
.y_axis(Axis::default().bounds(0.0, 1.0))
.hidden_legend_fraction(1.0, 1.0)
.style("on blue")
.width(6)
.height(3);
let out = console.render_export(&chart);
// Every cell on blue, the legend's box too.
assert!(out.starts_with("\u{1b}[44m"));Sourcepub fn legend_style(self, style: impl Into<String>) -> Self
pub fn legend_style(self, style: impl Into<String>) -> Self
The style of the legend’s box: a theme key or a definition (default:
the chart’s style when it has one, else the x
axis’s style). The names keep their datasets’ styles.
use rich_ext::chart::{Chart, Dataset};
let chart = Chart::new(vec![Dataset::new([(0.0, 1.0)]).name("left")])
.legend_style("dim");Sourcepub fn legend_unstyled(self) -> Self
pub fn legend_unstyled(self) -> Self
The legend’s box in the terminal’s own colours, not the x axis’s
style: a ratatui chart whose legend style is the default (scope-tui
sets legend_style(Style::default())). The same as
legend_style("none"), by name; the names
keep their datasets’ styles.
use rich::Console;
use rich_ext::chart::{Axis, Chart, Dataset};
let console = Console::builder().width(6).force_terminal(true).build();
let chart = Chart::new(vec![Dataset::new([]).name("a").style("none")])
.x_axis(Axis::default().bounds(0.0, 1.0).style("red"))
.y_axis(Axis::default().bounds(0.0, 1.0))
.hidden_legend_fraction(1.0, 1.0)
.width(6)
.height(3);
// The box in the x axis's red, then in no colour at all.
assert!(console.render_export(&chart).contains("\u{1b}[31m┌"));
assert!(!console.render_export(&chart.legend_unstyled()).contains('\u{1b}'));Sourcepub fn legend_clear(self, clear: bool) -> Self
pub fn legend_clear(self, clear: bool) -> Self
Whether the legend clears the plot under it (default true). When false, as in ratatui, what the datasets drew inside the box stays, unstyled, around the names.
use rich::Console;
use rich_ext::chart::{Axis, Chart, Dataset, Marker};
let console = Console::builder().width(8).color_system(None).build();
let wall = (0..8).flat_map(|x| (0..4).map(move |y| (f64::from(x), f64::from(y))));
let dots = Dataset::new(wall).name("a").marker(Marker::Ascii('.'));
let chart = Chart::new(vec![dots, Dataset::new([]).name("bb")])
.x_axis(Axis::default().bounds(0.0, 7.0))
.y_axis(Axis::default().bounds(0.0, 3.0))
.hidden_legend_fraction(1.0, 1.0)
.width(8)
.height(4);
assert_eq!(
console.render_export(&chart),
"....┌──┐\n....│a │\n....│bb│\n....└──┘\n"
);
assert_eq!(
console.render_export(&chart.legend_clear(false)),
"....┌──┐\n....│a.│\n....│bb│\n....└──┘\n"
);Sourcepub fn out_of_bounds(self, out_of_bounds: OutOfBounds) -> Self
pub fn out_of_bounds(self, out_of_bounds: OutOfBounds) -> Self
What to do with a segment that has an end outside the bounds
(default OutOfBounds::Clip).
Sourcepub fn layering(self, layering: Layering) -> Self
pub fn layering(self, layering: Layering) -> Self
How datasets that share a cell are put together (default
Layering::Merge).
Sourcepub fn marker(self, marker: Marker) -> Self
pub fn marker(self, marker: Marker) -> Self
The marker the axes’ grid lines are drawn with when
an axis gives none (Axis::grid_marker). Without it, the first
dataset’s marker, or Marker::Braille when there is no dataset,
so a chart’s grid looks the same before its first data as after.
use rich::Console;
use rich_ext::chart::{Axis, Chart, Dataset, Marker};
let console = Console::builder().width(4).color_system(None).build();
let empty = Chart::new(Vec::<Dataset>::new())
.x_axis(Axis::default().bounds(0.0, 1.0))
.y_axis(Axis::default().bounds(-1.0, 1.0).grid([0.0]))
.width(4)
.height(1);
// Braille with no datasets, not dots.
assert_eq!(console.render_export(&empty), "⠒⠒⠒⠒\n");
let ascii = empty.marker(Marker::Ascii('-'));
assert_eq!(console.render_export(&ascii), "----\n");Trait Implementations§
Source§impl AccessibleText for Chart
Chart, 2 datasets, x (t) from 0 to 10, y (%) from 0 to 4, then a line
per dataset: its name (dataset 2 without one), how it is drawn, and
its facts (3 points, min 0, max 4, last 1, trend up).
impl AccessibleText for Chart
Chart, 2 datasets, x (t) from 0 to 10, y (%) from 0 to 4, then a line
per dataset: its name (dataset 2 without one), how it is drawn, and
its facts (3 points, min 0, max 4, last 1, trend up).
Source§fn accessible_text(&self, _width: usize) -> String
fn accessible_text(&self, _width: usize) -> String
width where layout matters.Source§impl Renderable for Chart
impl Renderable for Chart
fn rich_render( &self, console: &Console, options: &ConsoleOptions, ) -> Vec<Segment>
Source§fn measure(&self, _console: &Console, options: &ConsoleOptions) -> Measurement
fn measure(&self, _console: &Console, options: &ConsoleOptions) -> Measurement
(minimum, maximum) cell width this renderable wants. Port of
__rich_measure__. The default is what upstream’s Measurement.get
returns for a renderable without one: anything from nothing to the
whole width, (0, max_width).Source§fn fit_to_measurement(&self) -> bool
fn fit_to_measurement(&self) -> bool
Console::print shrinks this renderable to its
measured width. Upstream renders every top-level renderable at the full
console width, so the default is false; an extension may opt in.Source§fn vertical(&self) -> Option<VerticalAlign>
fn vertical(&self) -> Option<VerticalAlign>
Table cell holding this renderable uses in
place of its column’s. Upstream reads getattr(renderable, "vertical", None); Align sets it.Source§fn print_end_line(&self, _options: &ConsoleOptions) -> bool
fn print_end_line(&self, _options: &ConsoleOptions) -> bool
"\n", so a print adds the final line break. A renderable
whose upstream output stops without one (Screen, LiveRender, a
Rule whose end has no newline) returns false, and the next
output continues its last line. options are the print’s options.