mirror of
https://git.alemi.dev/dashboard.git
synced 2024-11-26 01:14:50 +01:00
feat: made lines color customizable
This commit is contained in:
parent
cdb7d23695
commit
a0d33811b9
5 changed files with 99 additions and 41 deletions
|
@ -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>>,
|
||||
|
|
|
@ -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],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
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 {
|
||||
plot_ui.line(Line::new(source.values()).name(source.name.as_str()));
|
||||
Line::new(source.values()).name(source.name.as_str())
|
||||
};
|
||||
plot_ui.line(line.color(source.color));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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"];
|
||||
|
||||
|
@ -21,3 +22,21 @@ pub fn timestamp_to_str(t:i64) -> String {
|
|||
.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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue