Skip to content

Commit

Permalink
Refactor README.md to update ttydash usage guide and remove unnecessa…
Browse files Browse the repository at this point in the history
…ry unit options
  • Loading branch information
AndPuQing committed Oct 2, 2024
1 parent cc123c0 commit 346c267
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 83 deletions.
45 changes: 27 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,7 @@ while true; echo 1; sleep 0.5; end | ttydash -t "🌟 Title"
```
#### **Adding Units** (Optional)
If each line of data comes with a unit (e.g., "ms"), you can specify the unit with the `-u` flag. The unit can be one of the following:
- ⏱️ `ms` (milliseconds)
- ⏳ `s` (seconds)
- 📦 `mb` (megabytes)
- 🧑‍💻 `kb` (kilobytes)
- 💽 `gb` (gigabytes)
- 📊 `ki-b` (kibibytes)
If each line of data comes with a unit (e.g., "ms"), you can specify the unit with the `-u` flag.
Example 1️:
```bash
Expand All @@ -63,29 +56,45 @@ while true; echo "1 2 3"; sleep 0.5; end | ttydash
### 🎯 **Plot Specific Data Points** Using the `-i` Flag
If you only want to plot specific data points, you can use the `-i` flag to select their index. For example:
```bash
while true; echo "1 2 3"; sleep 0.5; end | ttydash -i 1 2
while true; echo "1 2 3"; sleep 0.5; end | ttydash -i 1 -i 2
```
In this example, only the data at **index 1** and **index 2** will be plotted.
👉 **Note**: You can switch the sequence of the index as needed. For example:
```bash
ttydash -i 2 1
ttydash -i 2 -i 1
```
This will plot **index 2** first, followed by **index 1**.
### 📈 **Group Chart**
```bash
while true; echo "1 2 3"; sleep 0.5; end | ttydash -g
```
![](./assets/group_chart.png)
## flags
```bash
A rust based tty plot
A terminal dashboard for custom monitoring.
Usage: ttydash [OPTIONS] [COMMAND]
Usage: ttydash [OPTIONS]
Commands:
add Add a new regex to the list of regexes
remove Remove a regex from the list of regexes
list List all regexes
help Print this message or the help of the given subcommand(s)
Options:
--tick-rate <FLOAT> Tick rate, i.e. number of ticks per second [default: 4]
-f, --frame-rate <FLOAT> Frame rate, i.e. number of frames per second [default: 60]
-t, --title <STRING> Chart title, will be shown at the top of the chart
-u, --unit <UNIT> Unit to be used in the chart [possible values: ms, s, mb, kb, gb, ki-b, mi-b, gi-b]
-h, --help Print help
-V, --version Print version
--tick-rate <FLOAT> Tick rate, i.e. number of ticks per second [default: 4]
-f, --frame-rate <FLOAT> Frame rate, i.e. number of frames per second [default: 60]
-t, --titles <STRING> Chart title, will be shown at the top of the chart
-u, --units <UNITS> Unit to be used in the chart (e.g. "ms", "MB")
-i, --indices <INT> Index vector to be used in the chart
-g, --group[=<GROUP>] Group together to show multiple charts in the same window [default: false] [possible values: true, false]
--update-frequency <INT> Update frequency, i.e. number of milliseconds between updates [default: 1000]
-h, --help Print help
-V, --version Print version
```
Binary file added assets/group_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 19 additions & 49 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,11 @@
use clap::ArgAction;
use clap::Args;
use clap::Parser;
use clap::Subcommand;
use clap::ValueEnum;
use strum::Display;

use crate::config::get_config_dir;
use crate::config::get_data_dir;

#[derive(Debug, Clone, Display)]
pub enum Unit {
Ms,
S,
MB,
KB,
GB,
KiB,
MiB,
GiB,
}

impl ValueEnum for Unit {
fn value_variants<'a>() -> &'a [Self] {
&[
Unit::Ms,
Unit::S,
Unit::MB,
Unit::KB,
Unit::GB,
Unit::KiB,
Unit::MiB,
Unit::GiB,
]
}

fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
Some(clap::builder::PossibleValue::new(self.to_string()))
}

fn from_str(input: &str, _ignore_case: bool) -> Result<Self, String> {
match input {
"ms" => Ok(Unit::Ms),
"s" => Ok(Unit::S),
"mb" => Ok(Unit::MB),
"kb" => Ok(Unit::KB),
"gb" => Ok(Unit::GB),
"kib" => Ok(Unit::KiB),
"mib" => Ok(Unit::MiB),
"gib" => Ok(Unit::GiB),
_ => Err(format!("Unknown unit: {}", input)),
}
}
}

#[derive(Parser, Debug)]
#[command(author, version = version(), about)]
pub struct Cli {
Expand All @@ -67,14 +21,30 @@ pub struct Cli {
#[arg(short, long, value_name = "STRING")]
pub titles: Option<Vec<String>>,

/// Unit to be used in the chart
/// Unit to be used in the chart (e.g. "ms", "MB")
#[arg(short, long)]
pub units: Option<Vec<Unit>>,
pub units: Option<Vec<String>>,

/// Index vector to be used in the chart
#[arg(short, long, value_name = "INT")]
pub indices: Option<Vec<usize>>,

/// Group together to show multiple charts in the same window
#[clap(
short,
long,
default_missing_value("true"),
default_value("false"),
num_args(0..=1),
require_equals(true),
action = ArgAction::Set,
)]
pub group: Option<bool>,

/// Update frequency, i.e. number of milliseconds between updates
#[arg(long, value_name = "INT", default_value_t = 1000)]
pub update_frequency: u64,

#[command(subcommand)]
pub cmd: Option<Commands>,
}
Expand Down
125 changes: 109 additions & 16 deletions src/components/dash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ use std::sync::{
};

use super::Component;
use crate::{
action::Action,
cli::{Cli, Unit},
};
use crate::{action::Action, cli::Cli};
use color_eyre::Result;

use libc::group;
use ratatui::{prelude::*, widgets::*};

use symbols::bar;
Expand Down Expand Up @@ -64,10 +62,11 @@ impl Default for DashState {
pub struct Dash {
bar_set: bar::Set,
update_frequency: u64,
group: bool,

state: Arc<RwLock<Vec<DashState>>>,
titles: Option<Vec<String>>,
units: Vec<Unit>,
units: Vec<String>,
indices: Option<Vec<usize>>,

command_tx: Option<UnboundedSender<Action>>,
Expand All @@ -93,9 +92,10 @@ impl Dash {
titles: args.titles,
state: Arc::new(RwLock::new(vec![DashState::default()])),
units,
group: args.group.unwrap_or(false),
indices: args.indices,
command_tx: None,
update_frequency: 1000,
update_frequency: args.update_frequency,
bar_set,
stop_signal: stop_signal.clone(),
};
Expand Down Expand Up @@ -167,6 +167,95 @@ impl Drop for Dash {
}

impl Dash {
fn draw_grouped_chart(&mut self, frame: &mut Frame, area: &Rect) -> Result<()> {
let state = self.state.read().unwrap();
let window_size = (area.width - 1) / state.len() as u16;

// Create time labels at intervals of 30, up to window_size - 5
let time_labels = (1..)
.map(|i| i * 30)
.take_while(|&t| t <= window_size - 5)
.collect::<Vec<_>>();

let mut span_vec = vec![];
let mut last_label_len = 0;

// Generate time markers for the chart axis
for &time in &time_labels {
let pos = window_size - time - 1;
if pos < window_size {
// Add spacing and time marker (e.g., "30s", "60s") with a line separator
span_vec.push(Span::raw("─".repeat(30 * state.len() - last_label_len)));
span_vec.push(Span::raw("├"));
span_vec.push(Span::styled(format!("{}s", time), Style::default().gray()));
last_label_len = format!("{}s", time).len() + 1;
}
}

span_vec.reverse(); // Reverse the order to display correctly on the chart

// Initialize the bar chart with styling and layout
let mut chart = BarChart::default()
.bar_set(self.bar_set.clone())
.bar_gap(0)
.block(
Block::default()
.border_type(BorderType::Rounded)
.title(Line::from("Group Chart").right_aligned()) // Add chart title
.title_bottom(Line::from(span_vec)) // Add time markers
.title_alignment(Alignment::Right)
.borders(Borders::ALL),
)
.bar_width(1)
.group_gap(0);

// Define a color map to style the bars
let color_map = vec![
Color::Green,
Color::Red,
Color::Yellow,
Color::Blue,
Color::Magenta,
Color::Cyan,
Color::White,
];

// Map the grouped bars and create bar groups
let grouped_bars = 0..window_size;

// Generate the bar groups with their corresponding data and styles
let mut bars = grouped_bars
.map(|i| {
BarGroup::default().bars(
&(0..state.len())
.map(|n| {
let state_n = &state[n];
// Fetch the value from state and create the Bar
let value =
state_n.data[state_n.data.len().saturating_sub((i + 1).into())];
Bar::default()
.value(value as u64)
.text_value("".to_owned()) // No text value displayed for the bars
.style(Style::default().fg(color_map[n % color_map.len()]))
// Style based on color map
})
.collect::<Vec<_>>(),
)
})
.collect::<Vec<_>>();

bars.reverse(); // Reverse the bar order to match display order

// Add each bar group to the chart
bars.iter().for_each(|bar_group| {
chart = chart.clone().data(bar_group.clone());
});

// Render the chart on the frame
frame.render_widget(chart, *area);
Ok(())
}

fn draw_chart(&mut self, frame: &mut Frame, area: &Rect, i: usize) -> Result<()> {
let title = self
.titles
Expand Down Expand Up @@ -251,17 +340,21 @@ impl Component for Dash {
}

fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let state = self.state.read().unwrap();

let num_chart_states = state.len();
// split the area
let chunks =
Layout::horizontal(vec![Constraint::Percentage(100); num_chart_states]).split(area);
// release the lock
drop(state);
for (i, chunk) in chunks.iter().enumerate() {
self.draw_chart(frame, chunk, i)?;
if !self.group {
let state = self.state.read().unwrap();
let num_chart_states = state.len();
// split the area
let chunks =
Layout::horizontal(vec![Constraint::Percentage(100); num_chart_states]).split(area);
// release the lock
drop(state);
for (i, chunk) in chunks.iter().enumerate() {
self.draw_chart(frame, chunk, i)?;
}
} else {
self.draw_grouped_chart(frame, &area)?;
}

Ok(())
}
}

0 comments on commit 346c267

Please sign in to comment.