feat: made lines color customizable

This commit is contained in:
əlemi 2022-06-08 14:05:18 +02:00
parent 203cff23f9
commit 4cc469f682
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 99 additions and 41 deletions

View file

@ -6,6 +6,7 @@ use std::sync::{RwLock, Mutex};
use std::num::ParseFloatError;
use chrono::{DateTime, Utc};
use eframe::egui::plot::{Values, Value};
use eframe::epaint::Color32;
use self::store::SQLiteDataStore;
@ -64,8 +65,9 @@ impl ApplicationState {
Ok(())
}
pub fn add_source(&self, panel_id:i32, name:&str, url:&str, query_x:&str, query_y:&str) -> Result<(), FetchError> {
let source = self.storage.lock().expect("Storage Mutex poisoned").new_source(panel_id, name, url, query_x, query_y)?;
pub fn add_source(&self, panel_id:i32, name:&str, url:&str, query_x:&str, query_y:&str, color:Color32, visible:bool) -> Result<(), FetchError> {
let source = self.storage.lock().expect("Storage Mutex poisoned")
.new_source(panel_id, name, url, query_x, query_y, color, visible)?;
let panels = self.panels.read().expect("Panels RwLock poisoned");
for panel in &*panels {
if panel.id == panel_id {
@ -97,6 +99,8 @@ pub struct Source {
pub name: String,
pub url: String,
pub interval: i32,
pub color: Color32,
pub visible: bool,
pub(crate) last_fetch: RwLock<DateTime<Utc>>,
pub query_x: String,
// pub(crate) compiled_query_x: Arc<Mutex<jq_rs::JqProgram>>,

View file

@ -1,8 +1,9 @@
use crate::app::data::{Panel, Source};
use crate::app::{data::{Panel, Source}, util::repack_color};
use chrono::{TimeZone, Utc};
use eframe::egui::plot::Value;
use eframe::egui::{Color32, plot::Value};
use rusqlite::{params, Connection};
use std::sync::RwLock;
use crate::app::util::unpack_color;
pub trait DataStorage {
fn add_panel(&self, name: &str);
@ -19,12 +20,12 @@ impl SQLiteDataStore {
conn.execute(
"CREATE TABLE IF NOT EXISTS panels (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE,
view_scroll BOOL,
view_size INT,
timeserie BOOL,
width INT,
height INT
name TEXT UNIQUE NOT NULL,
view_scroll BOOL NOT NULL,
view_size INT NOT NULL,
timeserie BOOL NOT NULL,
width INT NOT NULL,
height INT NOT NULL
);",
[],
)?;
@ -32,12 +33,14 @@ impl SQLiteDataStore {
conn.execute(
"CREATE TABLE IF NOT EXISTS sources (
id INTEGER PRIMARY KEY,
name TEXT,
url TEXT,
interval INT,
query_x TEXT,
query_y TEXT,
panel_id INT
name TEXT NOT NULL,
url TEXT NOT NULL,
interval INT NOT NULL,
query_x TEXT NOT NULL,
query_y TEXT NOT NULL,
panel_id INT NOT NULL,
color INT NULL,
visible BOOL NOT NULL
);",
[],
)?;
@ -45,10 +48,10 @@ impl SQLiteDataStore {
conn.execute(
"CREATE TABLE IF NOT EXISTS points (
id INTEGER PRIMARY KEY,
panel_id INT,
source_id INT,
x FLOAT,
y FLOAT
panel_id INT NOT NULL,
source_id INT NOT NULL,
x FLOAT NOT NULL,
y FLOAT NOT NULL
);",
[],
)?;
@ -105,6 +108,8 @@ impl SQLiteDataStore {
query_y: row.get(5)?,
// compiled_query_y: Arc::new(Mutex::new(jq_rs::compile(row.get::<usize, String>(5)?.as_str()).unwrap())),
// panel_id: row.get(6)?,
color: unpack_color(row.get(7).unwrap_or(0)),
visible: row.get(8)?,
data: RwLock::new(Vec::new()),
})
})?;
@ -127,10 +132,13 @@ impl SQLiteDataStore {
url: &str,
query_x: &str,
query_y: &str,
color: Color32,
visible: bool,
) -> rusqlite::Result<Source> {
let color_u32 : Option<u32> = if color == Color32::TRANSPARENT { None } else { Some(repack_color(color)) };
self.conn.execute(
"INSERT INTO sources(name, url, interval, query_x, query_y, panel_id) VALUES (?, ?, ?, ?, ?, ?)",
params![name, url, 60, query_x, query_y, panel_id],
"INSERT INTO sources(name, url, interval, query_x, query_y, panel_id, color, visible) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
params![name, url, 60i32, query_x, query_y, panel_id, color_u32, visible],
)?;
let mut statement = self
.conn
@ -141,17 +149,19 @@ impl SQLiteDataStore {
name: row.get(1)?,
url: row.get(2)?,
interval: row.get(3)?,
query_x: row.get(4)?,
query_y: row.get(5)?,
// panel_id: row.get(6)?,
last_fetch: RwLock::new(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)),
query_x: row.get(4)?,
// compiled_query_x: Arc::new(Mutex::new(jq_rs::compile(row.get::<usize, String>(4)?.as_str()).unwrap())),
query_y: row.get(5)?,
// compiled_query_y: Arc::new(Mutex::new(jq_rs::compile(row.get::<usize, String>(5)?.as_str()).unwrap())),
// panel_id: row.get(6)?,
color: unpack_color(row.get(7).unwrap_or(0)),
visible: row.get(8)?,
data: RwLock::new(Vec::new()),
})
})? {
if let Ok(p) = panel {
return Ok(p);
} else {
println!("WTF");
}
}
@ -166,10 +176,13 @@ impl SQLiteDataStore {
interval: i32,
query_x: &str,
query_y: &str,
color: Color32,
visible: bool,
) -> rusqlite::Result<usize> {
let color_u32 : Option<u32> = if color == Color32::TRANSPARENT { None } else { Some(repack_color(color)) };
self.conn.execute(
"UPDATE sources SET name = ?, url = ?, interval = ?, query_x = ?, query_y = ? WHERE id = ?",
params![name, url, interval, query_x, query_y, source_id],
"UPDATE sources SET name = ?, url = ?, interval = ?, query_x = ?, query_y = ?, color = ?, visible = ? WHERE id = ?",
params![name, url, interval, query_x, query_y, color_u32, visible, source_id],
)
}

View file

@ -5,7 +5,7 @@ pub mod util;
use std::sync::Arc;
use chrono::Utc;
use eframe::egui;
use eframe::egui::{plot::{Line, Plot}};
use eframe::egui::{RichText, plot::{Line, Plot}, Color32};
use self::data::ApplicationState;
use self::worker::native_save;
@ -18,6 +18,8 @@ struct InputBuffer {
interval: i32,
query_x: String,
query_y: String,
color: Color32,
visible: bool,
panel_id: i32,
}
@ -30,6 +32,8 @@ impl Default for InputBuffer {
interval: 60,
query_x: "".to_string(),
query_y: "".to_string(),
color: Color32::TRANSPARENT,
visible: true,
panel_id: 0,
}
}
@ -84,7 +88,9 @@ impl eframe::App for App {
}
}
);
ui.checkbox(&mut self.input.visible, "visible");
ui.add(egui::Slider::new(&mut self.input.interval, 1..=60));
ui.color_edit_button_srgba(&mut self.input.color);
if ui.button("add").clicked() {
self.data.add_source(
self.input.panel_id,
@ -92,6 +98,8 @@ impl eframe::App for App {
self.input.url.as_str(),
self.input.query_x.as_str(),
self.input.query_y.as_str(),
self.input.color,
self.input.visible,
).unwrap();
}
ui.separator();
@ -128,11 +136,19 @@ impl eframe::App for App {
ui.horizontal(|ui| {
ui.heading(panel.name.as_str());
ui.separator();
if !self.edit {
for source in &*sources {
ui.label(source.name.as_str());
ui.separator();
for source in &mut *sources {
if self.edit {
ui.checkbox(&mut source.visible, "");
}
if source.visible {
ui.label(
RichText::new(source.name.as_str())
.color(if source.color == Color32::TRANSPARENT { Color32::GRAY } else { source.color })
);
} else {
ui.label(RichText::new(source.name.as_str()).color(Color32::BLACK));
}
ui.separator();
}
ui.checkbox(&mut panel.view_scroll, "autoscroll");
ui.checkbox(&mut panel.timeserie, "timeserie");
@ -150,11 +166,12 @@ impl eframe::App for App {
for source in &mut *sources {
ui.horizontal(|ui| {
ui.add(egui::Slider::new(&mut source.interval, 1..=60));
eframe::egui::TextEdit::singleline(&mut source.url).hint_text("url").desired_width(250.0).show(ui);
eframe::egui::TextEdit::singleline(&mut source.url).hint_text("url").desired_width(300.0).show(ui);
if !panel.timeserie {
eframe::egui::TextEdit::singleline(&mut source.query_x).hint_text("x").desired_width(25.0).show(ui);
eframe::egui::TextEdit::singleline(&mut source.query_x).hint_text("x").desired_width(50.0).show(ui);
}
eframe::egui::TextEdit::singleline(&mut source.query_y).hint_text("y").desired_width(25.0).show(ui);
eframe::egui::TextEdit::singleline(&mut source.query_y).hint_text("y").desired_width(50.0).show(ui);
ui.color_edit_button_srgba(&mut source.color);
ui.separator();
ui.label(source.name.as_str());
});
@ -185,10 +202,13 @@ impl eframe::App for App {
p.show(ui, |plot_ui| {
for source in &mut *sources {
if self.filter {
plot_ui.line(Line::new(source.values_filter((Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64)).name(source.name.as_str()));
} else {
plot_ui.line(Line::new(source.values()).name(source.name.as_str()));
if source.visible {
let line = if self.filter {
Line::new(source.values_filter((Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64)).name(source.name.as_str())
} else {
Line::new(source.values()).name(source.name.as_str())
};
plot_ui.line(line.color(source.color));
}
}
});

View file

@ -1,5 +1,6 @@
// if you're handling more than terabytes of data, it's the future and you ought to update this code!
use chrono::{DateTime, Utc, NaiveDateTime};
use eframe::egui::Color32;
const PREFIXES: &'static [&'static str] = &["", "k", "M", "G", "T"];
@ -20,4 +21,22 @@ pub fn timestamp_to_str(t:i64) -> String {
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(t, 0), Utc)
.format("%Y/%m/%d %H:%M:%S")
)
}
pub fn unpack_color(c: u32) -> Color32 {
let r : u8 = (c >> 0) as u8;
let g : u8 = (c >> 8) as u8;
let b : u8 = (c >> 16) as u8;
let a : u8 = (c >> 24) as u8;
return Color32::from_rgba_unmultiplied(r, g, b, a);
}
pub fn repack_color(c: Color32) -> u32 {
let mut out : u32 = 0;
let mut offset = 0;
for el in c.to_array() {
out |= ((el & 0xFF) as u32) << offset;
offset += 8;
}
return out;
}

View file

@ -26,6 +26,8 @@ pub fn native_save(state:Arc<ApplicationState>) {
source.interval,
source.query_x.as_str(),
source.query_y.as_str(),
source.color,
source.visible,
).unwrap();
}
}