feat: multiple graphs

This commit is contained in:
electria 2026-08-15 18:16:16 -07:00
commit 98b7a8d201
Signed by: electria
SSH key fingerprint: SHA256:8LlB3ucPbBHqozqkhsNbaV5oG3SlzzqUj8FZDL6IPQs
4 changed files with 82 additions and 16 deletions

View file

@ -8,11 +8,23 @@ const CYAN: Color = Color::from_rgb8(0, 255, 255);
#[derive(Default)] #[derive(Default)]
pub struct CpuGraph { pub struct CpuGraph {
pub cache: canvas::Cache,
timeline: VecDeque<f32>, timeline: VecDeque<f32>,
pub cache: canvas::Cache,
} }
impl CpuGraph { impl CpuGraph {
pub fn new(usage: f32) -> Self {
Self {
timeline: {
let mut vec = VecDeque::new();
vec.push_front(usage);
vec
},
..Default::default()
}
}
pub fn update(&mut self, usage: f32) { pub fn update(&mut self, usage: f32) {
self.timeline.push_back(usage); self.timeline.push_back(usage);
if self.timeline.len() > 1000 { if self.timeline.len() > 1000 {
@ -32,6 +44,11 @@ impl canvas::Program<app::Message, Theme, Renderer> for CpuGraph {
_cursor: mouse::Cursor, _cursor: mouse::Cursor,
) -> Vec<canvas::Geometry<Renderer>> { ) -> Vec<canvas::Geometry<Renderer>> {
let geometry = self.cache.draw(renderer, bounds.size(), |frame| { let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
frame.stroke_rectangle(
Point::new(0., 0.),
bounds.size(),
canvas::Stroke::default().with_color(Color::from_rgb8(255, 0, 0)),
);
frame.stroke( frame.stroke(
&canvas::Path::new(|builder| { &canvas::Path::new(|builder| {
for (index, value) in self.timeline.iter().enumerate() { for (index, value) in self.timeline.iter().enumerate() {

View file

@ -4,24 +4,24 @@ use iced::{
}; };
use smol::Timer; use smol::Timer;
use crate::app::cpu_graph::CpuGraph; use crate::{app::cpu_graph::CpuGraph, sysinfo_snapshot::SysinfoSnapshot};
mod cpu_graph; mod cpu_graph;
enum Message { enum Message {
Cpu(f32), Info(SysinfoSnapshot),
} }
#[derive(Default)]
struct State { struct State {
cpu_graph: CpuGraph, total_cpu_graph: CpuGraph,
cpu_graphs: Vec<(String, CpuGraph)>,
} }
impl State { impl State {
fn new() -> (Self, Task<Message>) { fn new() -> (Self, Task<Message>) {
( (
Self { Self::default(),
cpu_graph: Default::default(),
},
Task::run( Task::run(
channel(1, async |mut sender| { channel(1, async |mut sender| {
let mut sys = sysinfo::System::new(); let mut sys = sysinfo::System::new();
@ -34,28 +34,58 @@ impl State {
loop { loop {
sys.refresh_specifics(refreshes); sys.refresh_specifics(refreshes);
Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await;
sender.send(sys.global_cpu_usage()).await.unwrap(); sender.send(SysinfoSnapshot::from(&sys)).await.unwrap();
} }
}), }),
Message::Cpu, Message::Info,
), ),
) )
} }
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::Cpu(value) => { Message::Info(snapshot) => {
self.cpu_graph.update(value); self.total_cpu_graph.update(snapshot.cpu_total);
self.cpu_graph.cache.clear(); self.total_cpu_graph.cache.clear();
if self.cpu_graphs.len() != snapshot.cpus.len() {
self.cpu_graphs = snapshot
.cpus
.into_iter()
.map(|(name, usage)| (name, CpuGraph::new(usage)))
.collect();
} else {
for ((_, graph), (_, usage)) in
self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter())
{
graph.update(usage);
graph.cache.clear();
}
}
} }
} }
Task::none() Task::none()
} }
fn view(&self) -> Element<'_, Message, Theme, Renderer> { fn view(&self) -> Element<'_, Message, Theme, Renderer> {
widget::Canvas::new(&self.cpu_graph) widget::row([
.width(Length::Fill) widget::Canvas::new(&self.total_cpu_graph)
.height(Length::Fill) .width(Length::Fill)
.into() .height(Length::Fill)
.into(),
widget::grid(self.cpu_graphs.iter().map(|(name, graph)| {
widget::row([
// widget::text(name).into(),
widget::Canvas::new(graph)
.width(Length::Fill)
.height(Length::Fill)
.into(),
])
.into()
}))
// .columns(8)
.into(),
])
.into()
} }
} }

View file

@ -1,4 +1,5 @@
mod app; mod app;
mod sysinfo_snapshot;
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
app::run() app::run()

18
src/sysinfo_snapshot.rs Normal file
View file

@ -0,0 +1,18 @@
#[derive(Clone, Debug)]
pub struct SysinfoSnapshot {
pub cpu_total: f32,
pub cpus: Vec<(String, f32)>,
}
impl From<&sysinfo::System> for SysinfoSnapshot {
fn from(value: &sysinfo::System) -> Self {
Self {
cpu_total: value.global_cpu_usage(),
cpus: value
.cpus()
.iter()
.map(|cpu| (cpu.name().into(), cpu.cpu_usage()))
.collect(),
}
}
}