Working GUI with async load and progressbar

This commit is contained in:
David Holland 2023-01-31 19:23:00 +01:00
parent 1fd5833d19
commit 710c9400a6
10 changed files with 437 additions and 160 deletions

10
Cargo.lock generated
View File

@ -2265,6 +2265,15 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "poll-promise"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf2a02372dfae23c9c01267fb296b8a3413bb4e45fbd589c3ac73c6dcfbb305"
dependencies = [
"static_assertions",
]
[[package]]
name = "polling"
version = "2.5.2"
@ -2472,6 +2481,7 @@ dependencies = [
"notify-rust",
"palette",
"plotly",
"poll-promise",
"rfd",
"serde",
"serde_json",

View File

@ -3,7 +3,7 @@ use serde::Deserialize;
use std::collections::HashMap;
use std::fs;
#[derive(Deserialize, Debug)]
#[derive(Deserialize, Debug, Default)]
pub struct Shot {
pub filename: Option<String>,
pub json: Option<String>,
@ -12,7 +12,7 @@ pub struct Shot {
pub disable: Option<bool>,
}
#[derive(Deserialize, Debug)]
#[derive(Deserialize, Debug, Default)]
pub struct Chart {
pub title: String,
pub shots: Vec<u64>,
@ -22,7 +22,7 @@ pub struct Chart {
pub max_flow: u64,
}
#[derive(Deserialize, Debug)]
#[derive(Deserialize, Debug, Default)]
pub struct Config {
pub shots: HashMap<String, Shot>,
pub charts: HashMap<String, Chart>,

View File

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use std::fs;
use crate::config;
use crate::time::{unix_to_human_date, unix_to_human_time};
use crate::time::{unix_to_human_date, unix_to_human_date_time, unix_to_human_time};
/// Structs for handling the Beanconqueror database "Beanconqueror.json" export data
@ -84,20 +84,30 @@ impl Database {
pub fn bean_names_with_uuids(&self) -> Vec<(String, String)> {
self.beans
.iter()
.map(|bean| (bean.name_with_date(), bean.config.uuid.clone()))
.map(|bean| (bean.name_with_date(), bean.config.uuid.to_owned()))
.collect()
}
pub fn bean_for_uuid(&self, uuid: &str) -> Option<&Bean> {
self.beans
.iter()
.find(|bean| bean.config.uuid == uuid.to_string())
.find(|bean| bean.config.uuid == uuid.to_owned())
}
pub fn brew_for_uuid(&self, uuid: &str) -> Option<&Brew> {
self.brews
.iter()
.find(|brew| brew.config.uuid == uuid.to_string())
.find(|brew| brew.config.uuid == uuid.to_owned())
}
pub fn brews_for_uuids(&self, uuids: &Vec<String>) -> Vec<&Brew> {
self.brews
.iter()
.filter_map(|brew| match uuids.contains(&brew.config.uuid) {
true => Some(brew),
false => None,
})
.collect()
}
pub fn bean_for_brew(&self, brew: &Brew) -> Option<&Bean> {
@ -120,14 +130,34 @@ impl Bean {
pub fn name_with_date(&self) -> String {
format!(
"{} from {}",
self.name.clone(),
unix_to_human_date(self.config.unix_timestamp.clone())
self.name.to_owned(),
unix_to_human_date(self.config.unix_timestamp.to_owned())
)
}
}
impl Brew {
pub fn name_with_time(&self) -> String {
unix_to_human_time(self.config.unix_timestamp.clone())
pub fn time(&self) -> String {
unix_to_human_time(self.config.unix_timestamp.to_owned())
}
pub fn date_time(&self, database: &Database) -> String {
if let Some(bean) = &database.bean_for_uuid(&self.bean) {
unix_to_human_date_time(self.config.unix_timestamp.to_owned())
} else {
String::default()
}
}
pub fn date_time_with_bean(&self, database: &Database) -> String {
if let Some(bean) = &database.bean_for_uuid(&self.bean) {
format!(
"{} ({})",
unix_to_human_date_time(self.config.unix_timestamp.to_owned()),
bean.name_with_date()
)
} else {
String::default()
}
}
}

View File

@ -99,10 +99,10 @@ impl FlowProfile {
ref actual_weight,
..
}| {
let deltatime = deltatime(str_to_naivetime(timestamp.as_str()), reference_time);
let deltatime = deltatime(str_to_naivetime(&timestamp), reference_time);
let std_duration = deltatime.to_std().unwrap();
(std_duration.as_secs_f64(), actual_weight.clone())
(std_duration.as_secs_f64(), actual_weight.to_owned())
},
)
.collect();
@ -116,10 +116,10 @@ impl FlowProfile {
ref value,
..
}| {
let deltatime = deltatime(str_to_naivetime(timestamp.as_str()), reference_time);
let deltatime = deltatime(str_to_naivetime(&timestamp), reference_time);
let std_duration = deltatime.to_std().unwrap();
(std_duration.as_secs_f64(), value.clone())
(std_duration.as_secs_f64(), value.to_owned())
},
)
.collect();
@ -133,10 +133,10 @@ impl FlowProfile {
ref flow_value,
..
}| {
let deltatime = deltatime(str_to_naivetime(timestamp.as_str()), reference_time);
let deltatime = deltatime(str_to_naivetime(&timestamp), reference_time);
let std_duration = deltatime.to_std().unwrap();
(std_duration.as_secs_f64(), flow_value.clone())
(std_duration.as_secs_f64(), flow_value.to_owned())
},
)
.collect();

View File

@ -1,10 +1,10 @@
mod config;
mod ui;
mod database;
mod flow_profile;
mod plot;
mod sheets;
mod time;
mod ui;
use ui::Ui;
@ -16,7 +16,7 @@ use std::panic;
use eframe::egui;
fn main() -> Result<(), eframe::Error>{
fn main() -> Result<(), eframe::Error> {
panic::set_hook(Box::new(console_error_panic_hook::hook));
// generate_plots();

View File

@ -1,11 +1,12 @@
use crate::config::Config;
use crate::database::Database;
use crate::database::{Brew, Database};
use crate::flow_profile::FlowProfile;
use crate::sheets::load_data;
use crate::time::{unix_to_human_date, unix_to_human_date_time, unix_to_machine_date};
use dialoguer::{theme::ColorfulTheme, MultiSelect};
use egui::plot::{PlotPoint, PlotPoints};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use notify_rust::Notification;
@ -15,7 +16,20 @@ use plotly::{
Layout, Plot, Scatter,
};
use std::collections::HashMap;
use std::fs;
use std::sync::{Arc, Mutex};
pub type RawPlotPoints = Vec<PlotPoint>;
pub type PlotEntry = (String, RawPlotPoints);
#[derive(Default)]
pub struct LoadingProgress {
pub curr_name: String,
pub current: usize,
pub total: usize,
pub percentage: f32,
}
pub fn generate_plots() -> Vec<(String, Plot)> {
let config_file = fs::read_to_string("config.toml").expect("Can't read config.toml");
@ -83,6 +97,61 @@ pub fn generate_plots() -> Vec<(String, Plot)> {
result
}
pub fn plot_points_to_owned(plot_points: &RawPlotPoints) -> PlotPoints {
PlotPoints::Owned(plot_points.to_owned())
}
pub fn database_plot_entry(brew: &Brew, config: &Config, database: &Database) -> Option<PlotEntry> {
let mut plot_points: Option<(String, RawPlotPoints)> = None;
if let Some(flow_profile) = &brew.flow_profile {
if !&flow_profile.is_empty() {
let brew_title = brew.date_time_with_bean(&database);
let flow_profile =
FlowProfile::from_file(&format!("{}/{}", &config.data_dir, &flow_profile), None)
.preprocess_json();
if let Some(data_collection) = &flow_profile.data_collection {
let point_vec: Vec<[f64; 2]> = data_collection
.weight
.iter()
.map(|(x, y)| [x.to_owned(), y.to_owned()])
.collect();
plot_points = Some((brew_title, PlotPoints::from(point_vec).points().to_vec()));
}
}
}
plot_points
}
pub fn database_plot_entries(
brews: Vec<&Brew>,
config: &Config,
database: &Database,
progress: Option<Arc<Mutex<LoadingProgress>>>,
) -> HashMap<String, PlotEntry> {
let mut plot_entries: HashMap<String, PlotEntry> = HashMap::new();
let step_size = 1.0 / brews.len() as f32;
for (i, brew) in brews.iter().enumerate() {
if let Some(progress) = &progress {
let mut progress_lock = progress.lock().unwrap();
(*progress_lock).curr_name = brew.date_time_with_bean(&database);
(*progress_lock).current = i;
(*progress_lock).total = brews.len();
(*progress_lock).percentage += step_size;
}
if let Some(plot_entry) = database_plot_entry(&brew, &config, &database) {
plot_entries.insert(brew.config.uuid.to_owned(), plot_entry);
}
}
plot_entries
}
pub fn database_plot_selected(uuids: Vec<String>, config: &Config) -> Vec<(String, Plot)> {
let mut result: Vec<(String, Plot)> = Vec::with_capacity(config.charts.len());
@ -93,7 +162,7 @@ pub fn database_plot_selected(uuids: Vec<String>, config: &Config) -> Vec<(Strin
if !uuids.is_empty() {
for single_selection in uuids {
if let Some(bean) = &database.bean_for_uuid(&single_selection) {
let bean_timestamp = bean.config.unix_timestamp.clone();
let bean_timestamp = bean.config.unix_timestamp.to_owned();
let title = format!("{} from {}", &bean.name, unix_to_human_date(bean_timestamp));
let filename = format!(
@ -111,7 +180,7 @@ pub fn database_plot_selected(uuids: Vec<String>, config: &Config) -> Vec<(Strin
if let Some(flow_profile) = &brew.flow_profile {
if !&flow_profile.is_empty() {
let brew_title =
unix_to_human_date_time(brew.config.unix_timestamp.clone());
unix_to_human_date_time(brew.config.unix_timestamp.to_owned());
let brew = FlowProfile::from_file(
&format!("{}/{}", &config.data_dir, &flow_profile),
@ -145,7 +214,7 @@ pub fn database_plot_selected(uuids: Vec<String>, config: &Config) -> Vec<(Strin
plot.use_local_plotly();
plot.write_html(filename);
result.push((title.to_string(), plot));
result.push((title.to_owned(), plot));
}
// progress_beans.inc(..);
@ -187,14 +256,14 @@ pub fn database_plot_selected_tui() -> Vec<(String, Plot)> {
.expect("Can't generate progress bar style");
let progress_beans = multi_progress.add(
ProgressBar::new(selection.len() as u64)
.with_style(progress_style.clone())
.with_style(progress_style.to_owned())
.with_message("Beans"),
);
let mut progress_brews: ProgressBar = ProgressBar::new(0);
for single_selection in selection {
if let Some(bean) = &database.beans.get(single_selection) {
let bean_timestamp = bean.config.unix_timestamp.clone();
let bean_timestamp = bean.config.unix_timestamp.to_owned();
let title = format!("{} from {}", &bean.name, unix_to_human_date(bean_timestamp));
let filename = format!(
@ -211,7 +280,7 @@ pub fn database_plot_selected_tui() -> Vec<(String, Plot)> {
progress_brews = multi_progress.insert_after(
&progress_beans,
ProgressBar::new(brews.len() as u64)
.with_style(progress_style.clone())
.with_style(progress_style.to_owned())
.with_message("Brews"),
);
@ -219,7 +288,7 @@ pub fn database_plot_selected_tui() -> Vec<(String, Plot)> {
if let Some(flow_profile) = &brew.flow_profile {
if !&flow_profile.is_empty() {
let brew_title =
unix_to_human_date_time(brew.config.unix_timestamp.clone());
unix_to_human_date_time(brew.config.unix_timestamp.to_owned());
let brew = FlowProfile::from_file(
&format!("{}/{}", &config.data_dir, &flow_profile),
@ -253,7 +322,7 @@ pub fn database_plot_selected_tui() -> Vec<(String, Plot)> {
plot.use_local_plotly();
plot.write_html(filename);
result.push((title.to_string(), plot));
result.push((title.to_owned(), plot));
}
progress_beans.inc(1);

View File

@ -70,7 +70,7 @@ impl Sandbox for Selector {
.items
.iter()
.filter(|item| item.checked)
.map(|item| item.uuid.clone())
.map(|item| item.uuid.to_owned())
.collect(),
);
}

View File

@ -28,7 +28,7 @@ pub fn unix_to_human_date_time(unix_timestamp: i64) -> String {
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
date_time.format("%b %d, %Y %I:%M %P").to_string()
} else {
"Unknown date & time".to_string()
"Unknown date & time".to_owned()
}
}
@ -36,7 +36,7 @@ pub fn unix_to_human_date(unix_timestamp: i64) -> String {
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
date_time.format("%b %d, %Y").to_string()
} else {
"Unknown date".to_string()
"Unknown date".to_owned()
}
}
@ -44,7 +44,7 @@ pub fn unix_to_human_time(unix_timestamp: i64) -> String {
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
date_time.format("%I:%M %P").to_string()
} else {
"Unknown time".to_string()
"Unknown time".to_owned()
}
}
@ -52,6 +52,6 @@ pub fn unix_to_machine_date(unix_timestamp: i64) -> String {
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
date_time.format("%F").to_string()
} else {
"unknown".to_string()
"unknown".to_owned()
}
}

372
src/ui.rs
View File

@ -2,15 +2,35 @@
use eframe::egui;
use egui::{Align, Layout};
use egui::{
plot::{Legend, Line, Plot},
Align, Layout, ProgressBar,
};
use rfd::FileDialog;
use crate::{
config::Config,
database::{Bean, Database},
database::{Brew, Database},
plot::{self, plot_points_to_owned, LoadingProgress, PlotEntry},
};
use std::collections::HashMap;
use std::{
collections::HashMap,
thread::{self, JoinHandle},
};
use std::{
env,
sync::{Arc, Mutex},
};
#[derive(Default, Debug)]
struct LoadingData {
config: Config,
database: Database,
brew_checkboxes: HashMap<String, bool>,
selected_bean: String,
plot_entries: HashMap<String, PlotEntry>,
}
#[derive(Default)]
pub struct Ui {
@ -19,15 +39,54 @@ pub struct Ui {
side_panel_expanded: bool,
database_loaded: bool,
config: Option<Config>,
database: Option<Database>,
continuous_mode: bool,
loading_progress: Arc<Mutex<LoadingProgress>>,
loader_thread: Option<JoinHandle<()>>,
loading_data: Arc<Mutex<Option<LoadingData>>>,
data_loaded: bool,
selected_bean:
bean_checkboxes: Option<HashMap<String, bool>>,
brew_checkboxes: Option<HashMap<String, bool>>,
config: Config,
database: Database,
selected_bean: String,
brew_checkboxes: HashMap<String, bool>,
select_all: bool,
clear_all: bool,
picked_path: Option<String>,
plot_entries: HashMap<String, PlotEntry>,
}
impl Ui {
pub fn get_selected_brew_uuids(&self) -> Vec<String> {
self.brew_checkboxes
.iter()
.filter_map(|(uuid, checked)| match checked {
true => Some(uuid.to_owned()),
false => None,
})
.collect::<Vec<String>>()
}
pub fn get_selected_brews(&self) -> Vec<&Brew> {
self.database
.brews_for_uuids(&self.get_selected_brew_uuids())
}
pub fn reload(&mut self, ctx: &egui::Context) {
if let Some(loader_thread) = &self.loader_thread {
if loader_thread.is_finished() {
self.data_loaded = false;
self.loader_thread = None;
let loading_progress_arc = self.loading_progress.clone();
let mut loading_progress_lock = loading_progress_arc.lock().unwrap();
*loading_progress_lock = LoadingProgress::default();
}
}
}
}
impl eframe::App for Ui {
@ -37,19 +96,51 @@ impl eframe::App for Ui {
}
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
if self.continuous_mode {
ctx.request_repaint();
}
self.allowed_to_close = true;
egui::TopBottomPanel::bottom("status_bar").show(ctx, |ui| {
ui.columns(2, |columns| {});
ui.columns(2, |_columns| {});
});
egui::TopBottomPanel::top("dark_light").show(ctx, |ui| {
ui.columns(2, |columns| {
columns[0].heading("RustyBeans");
columns[1].with_layout(Layout::right_to_left(Align::Min), |ui| {
egui::widgets::global_dark_light_mode_buttons(ui);
ui.vertical(|ui| {
egui::menu::bar(ui, |ui| {
if ui
.button(match self.side_panel_expanded {
true => "<",
false => ">",
})
.clicked()
{
self.side_panel_expanded = !self.side_panel_expanded;
}
let reload_button = ui.button("Reload");
if reload_button.clicked() {
self.reload(ctx);
}
if reload_button.hovered() {
if let Some(loader_thread) = &self.loader_thread {
if !loader_thread.is_finished() {
egui::show_tooltip(ctx, egui::Id::new("reload_tooltip"), |ui| {
ui.label("Loading is still in progress.\nTo reload, please wait until previous loading has finished!");
});
}
}
}
ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
egui::widgets::global_dark_light_mode_buttons(ui);
ui.heading("RustyBeans");
});
});
});
})
});
egui::SidePanel::left("selection_panel")
@ -60,52 +151,64 @@ impl eframe::App for Ui {
ui.horizontal_centered(|ui| {
ui.vertical(|ui| {
if let Some(bean_checkboxes) = self.bean_checkboxes.as_mut() {
if let Some(database) = &self.database {
ui.heading("Beans");
if self.data_loaded {
ui.heading("Beans");
egui::ScrollArea::vertical()
.id_source("beans_scroll")
.max_height(total_height / 2.0)
.show(
ui,
|ui| {
for bean in &database.beans {
ui.horizontal(|ui| {
if let Some(checked) = bean_checkboxes
.get_mut(&bean.config.uuid)
{
ui.checkbox(
checked,
bean.name_with_date(),
);
}
});
egui::ScrollArea::vertical()
.id_source("beans_scroll")
.max_height(total_height / 2.0)
.show(ui, |ui| {
for bean in &self.database.beans {
ui.horizontal(|ui| {
if ui
.radio_value(
&mut self.selected_bean,
bean.config.uuid.to_owned(),
bean.name_with_date(),
)
.clicked_by(egui::PointerButton::Primary)
{
if ctx.input(|i| i.modifiers.ctrl) {
self.select_all = true;
}
}
},
);
}
});
}
});
}
});
ui.vertical(|ui| {
if let Some(brew_checkboxes) = self.brew_checkboxes.as_mut() {
if let Some(database) = &self.database {
if self.data_loaded {
if let Some(bean) =
&self.database.bean_for_uuid(&self.selected_bean)
{
let brews = self.database.brews_for_bean(&bean);
ui.heading("Brews");
egui::ScrollArea::vertical()
.id_source("brews_scroll")
.max_height(total_height / 2.0)
.show(ui, |ui| {
for brew in &database.brews {
for brew in &brews {
ui.horizontal(|ui| {
if let Some(checked) =
brew_checkboxes.get_mut(&brew.config.uuid)
if let Some(checked) = self
.brew_checkboxes
.get_mut(&brew.config.uuid)
{
ui.checkbox(checked, brew.name_with_time());
if self.select_all {
*checked = !*checked;
}
ui.checkbox(
checked,
brew.date_time(&self.database),
);
}
});
}
self.select_all = false;
});
}
}
@ -114,25 +217,49 @@ impl eframe::App for Ui {
ui.horizontal_centered(|ui| {
ui.vertical(|ui| {
if let Some(brew_checkboxes) = self.brew_checkboxes.as_mut() {
if let Some(database) = &self.database {
ui.heading("Selected Brews");
if self.data_loaded {
ui.heading("Selected Brews");
egui::ScrollArea::vertical()
.id_source("selected_brews_scroll")
.max_height(total_height / 2.0)
.show(ui, |ui| {
for brew in &database.brews {
ui.horizontal(|ui| {
if let Some(checked) =
brew_checkboxes.get_mut(&brew.config.uuid)
{
ui.checkbox(checked, brew.name_with_time());
egui::ScrollArea::vertical()
.id_source("selected_brews_scroll")
.max_height(total_height / 2.0)
.show(ui, |ui| {
if self.clear_all {
self.brew_checkboxes.iter_mut().for_each(
|brew_checkbox| {
*brew_checkbox.1 = false;
},
);
self.clear_all = false;
} else {
for brew in &self.database.brews {
if let Some(checked) =
self.brew_checkboxes.get_mut(&brew.config.uuid)
{
if *checked {
ui.horizontal(|ui| {
if ui
.checkbox(
checked,
brew.date_time_with_bean(
&self.database,
),
)
.clicked_by(
egui::PointerButton::Primary,
)
{
if ctx.input(|i| i.modifiers.ctrl) {
self.clear_all = true;
}
}
});
}
});
}
}
});
}
}
});
}
});
});
@ -162,31 +289,118 @@ impl eframe::App for Ui {
Layout::top_down(Align::Center).with_main_align(egui::Align::Center),
|ui| {
if let Some(picked_path) = &self.picked_path {
if !self.database_loaded {
self.config = Some(Config::from_file(&picked_path));
let thread_handle = self.loader_thread.get_or_insert_with(|| {
self.continuous_mode = true;
if let Some(config) = &self.config {
self.database = Some(Database::from_config(&config));
let loading_data_arc = self.loading_data.clone();
let loading_progress_arc = self.loading_progress.clone();
let picked_path_owned = picked_path.to_owned();
let ctx_clone = ctx.clone();
if let Some(database) = &self.database {
let mut checkboxes = HashMap::new();
thread::spawn(move || {
let ctx = ctx_clone;
for bean in &database.beans {
checkboxes.insert(bean.config.uuid.clone(), false);
}
let mut loading_data = LoadingData::default();
self.bean_checkboxes = Some(checkboxes);
loading_data.config = Config::from_file(&picked_path_owned);
checkboxes = HashMap::new();
loading_data.database =
Database::from_config(&loading_data.config);
for brew in &database.brews {
checkboxes.insert(brew.config.uuid.clone(), false);
}
let mut checkboxes = HashMap::new();
self.brew_checkboxes = Some(checkboxes);
self.database_loaded = true;
for brew in &loading_data.database.brews {
checkboxes.insert(brew.config.uuid.to_owned(), false);
}
loading_data.brew_checkboxes = checkboxes;
if !&loading_data.database.beans.is_empty() {
loading_data.selected_bean =
loading_data.database.beans[0].config.uuid.to_owned();
}
loading_data.plot_entries = plot::database_plot_entries(
loading_data
.database
.brews
.iter()
.map(|brew| brew)
.collect(),
&loading_data.config,
&loading_data.database,
Some(loading_progress_arc),
);
let mut loading_data_lock = loading_data_arc.lock().unwrap();
*loading_data_lock = Some(loading_data);
ctx.request_repaint();
})
});
if !thread_handle.is_finished() {
egui::Window::new("Loading resources")
.collapsible(false)
.resizable(false)
.show(ctx, |ui| {
let loading_progress_arc = self.loading_progress.clone();
let loading_progress_lock =
loading_progress_arc.lock().unwrap();
ui.add(
ProgressBar::new(
loading_progress_lock.percentage.to_owned(),
)
.text(
format!(
"{:.0}% | Brew [{}/{}]",
loading_progress_lock.percentage * 100.0,
loading_progress_lock.current,
loading_progress_lock.total
),
),
);
});
} else {
if self.continuous_mode {
self.continuous_mode = false;
}
if !self.data_loaded {
self.side_panel_expanded = true;
// println!("loading_data: {:?}", &self.loading_data);
let mut loading_data_lock = self.loading_data.lock().unwrap();
// println!("loading_data_lock: {:?}", loading_data_lock);
let loading_data = loading_data_lock.take().unwrap();
self.config = loading_data.config;
self.database = loading_data.database;
self.brew_checkboxes = loading_data.brew_checkboxes;
self.selected_bean = loading_data.selected_bean;
self.plot_entries = loading_data.plot_entries;
self.data_loaded = true;
} else {
Plot::new("Combined Chart of selected brews")
.legend(Legend::default())
.show(ui, |plot_ui| {
for brew_checkbox in &self.brew_checkboxes {
if *brew_checkbox.1 {
if let Some(plot_points) =
self.plot_entries.get(brew_checkbox.0)
{
plot_ui.line(
Line::new(plot_points_to_owned(
&plot_points.1,
))
.name(plot_points.0.to_owned()),
);
}
}
}
});
}
}
} else {
@ -207,6 +421,10 @@ impl eframe::App for Ui {
self.picked_path = Some(path.display().to_string());
}
}
if ui.button("Open default config.toml").clicked() {
self.picked_path = Some(String::from("config.toml"));
}
}
},
);

View File

@ -1,50 +0,0 @@
use yew::prelude::*;
use crate::config::Config;
use crate::database::Database;
use crate::plot::database_plot_selected;
use crate::plot::database_plot_selected_tui;
use std::process::Command;
#[function_component(App)]
pub fn plot_component() -> Html {
let config = Config::from_default();
let database = Database::from_config(&config);
let plots = database_plot_selected(
database
.brews
.iter()
.map(|brew| brew.config.uuid.clone())
.collect(),
true,
);
let plot_names: Vec<String> = plots.clone().into_iter().map(|plot| plot.0.clone()).collect();
let p = yew_hooks::use_async::<_, _, ()>({
async move {
for plot in &plots {
plotly::bindings::new_plot(&plot.0, &plot.1).await;
}
Ok(())
}
});
use_effect_with_deps(
move |_| {
p.run();
|| ()
},
(),
);
html! {
{
for plot_names.into_iter().map(|name| {
html!{<div id={name}></div>}
})
}
}
}