Compare commits

...

2 Commits

Author SHA1 Message Date
David Holland e6f47328a0 egui wasm32 working, except file loading 2023-09-14 22:26:01 +02:00
David Holland 9124e147f2 Some differing egui changes? 2023-09-14 11:09:05 +02:00
84 changed files with 654 additions and 13213 deletions

2
.gitignore vendored
View File

@ -7,7 +7,9 @@
!/config.toml
!/index.html
!/rust-toolchain.toml
!/Trunk.toml
!/assets/
!/beanconqueror/
!/shots/
!/src/

964
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
[package]
authors = ["David Holland <info@dustvoice.de>"]
name = "rustybeans"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -12,26 +12,22 @@ chrono = { version = "0.4.23", features = ["wasmbind"] }
console_error_panic_hook = "0.1.7"
fast-float = "0.2.0"
palette = "0.6.1"
plotly = { version = "0.8.1", features = ["wasm"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.5.9"
indicatif = "0.17.3"
dialoguer = "0.10.3"
notify-rust = "4.7.0"
crossterm = "0.25.0"
rfd = "0.10.0"
egui = "0.22.0"
eframe = "0.22.0"
log = "0.4.20"
async-std = "1.12.0"
zip = "0.6.6"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tracing-subscriber = "0.3"
env_logger = "0.10"
# web:
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"
tracing-wasm = "0.2"
wasm-bindgen-futures = "0.4"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

View File

@ -1,161 +0,0 @@
import pandas as pd
from datetime import datetime
import numpy as np
from numpy import diff
from scipy.interpolate import make_interp_spline
from scipy.signal import savgol_filter
import matplotlib.pyplot as plt
timestamp_start = None
def unix_to_datetime(unix_string):
return datetime.strptime(unix_string, "%H:%M:%S.%f")
def deltatime(time):
global timestamp_start
return (time - timestamp_start).total_seconds()
def timestamp_converter(timestamp):
time = unix_to_datetime(timestamp)
global timestamp_start
if timestamp_start == None:
timestamp_start = time
delta = deltatime(time)
return delta
shots = [
{
'filename': 'Elisabeth/16_06_2022_1.xlsx',
'title': 'Elisabeth 16.06.2022 #1',
'cutoff': 38,
}, # [0]
{
'filename': 'Elisabeth/19_06_2022_1.xlsx',
'title': 'Elisabeth 19.06.2022 #1',
}, # [1]
{
'filename': 'Laura/20_06_2022_1.xlsx',
'title': 'Laura 20.06.2022 #1',
'cutoff': 38.7,
}, # [2]
{
'filename': 'Laura/20_06_2022_2.xlsx',
'title': 'Laura 20.06.2022 #2',
'cutoff': 32.6,
}, # [3]
]
comparison = {
'Elisabeth 19.06.22 Shot #1 vs. #2': (0, 1), #[0]
'Laura 20.06.22 Shot #1 vs #2': (2, 3), #[1]
#'comparison': {
# 'Elisabeth Shots 06/13/22 - 06/10/22': (1, 2), #[0]
# 'Elisabeth Shots 06/15/22 - 06/10/22': (1, 3), #[1]
# 'Elisabeth Shots 06/15/22 - 06/13/22': (2, 3), #[2]
# }
}
curr_figure = 0
fig1 = plt.figure(curr_figure)
ax1 = plt.subplot(2, 2, 1)
# ax1.minorticks_on()
# plt.title('Shotweight over time')
plt.ylabel('Weight (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
ax2 = plt.subplot(2, 2, 3, sharex=ax1)
# plt.title('Flow-rate over time')
plt.ylabel('Flow-rate (g/s)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
ax3 = plt.subplot(2, 2, 2, sharex=ax1, sharey=ax1)
# plt.title('Shotweight over time')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
ax4 = plt.subplot(2, 2, 4, sharex=ax2, sharey=ax2)
# plt.title('Flow-rate over time')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
fig1.supxlabel('Time (s)')
plt.tight_layout()
plt.subplot(2, 2, 1)
for shot in shots:
timestamp_start = None
df_raw = pd.read_excel(shot['filename'], converters={
0: lambda x: timestamp_converter(x)
}, sheet_name=0)
df_calc = pd.read_excel(shot['filename'], converters={
0: lambda x: timestamp_converter(x)
}, sheet_name=1)
time_col_raw = df_raw.keys()[0]
profile_col_raw = df_raw.keys()[5]
time_col_calc = df_calc.keys()[0]
profile_col_calc = df_calc.keys()[2]
if 'cutoff' in shot:
if shot['cutoff'] != -1:
df_raw = df_raw.loc[df_raw[time_col_raw] < shot['cutoff']]
df_calc = df_calc.loc[df_calc[time_col_calc] < shot['cutoff']]
shot['data'] = {}
shot['data']['profile'] = df_raw
shot['data']['flowrate'] = df_calc
time = df_raw[time_col_raw].tolist()
weight = df_raw[profile_col_raw].tolist()
# print("time: ", time)
# print("weight: ", time)
plt.plot(time, weight, label = shot['title'], linewidth=1)
plt.subplot(2, 2, 3)
flow_time = df_calc[time_col_calc].tolist()
flowrate = df_calc[profile_col_calc].tolist()
# print("flow_time: ", flow_time)
# print("flowrate: ", flowrate)
plt.plot(flow_time, flowrate, label = shot['title'], linewidth=1)
plt.subplot(2, 2, 1)
plt.subplot(2, 2, 4)
for key in comparison:
calc1 = shots[comparison[key][0]]['data']['flowrate']
calc2 = shots[comparison[key][1]]['data']['flowrate']
t1 = np.array(calc1[calc1.keys()[0]].tolist())
r1 = np.array(calc1[calc1.keys()[2]].tolist())
t2 = np.array(calc2[calc2.keys()[0]].tolist())
r2 = np.array(calc2[calc2.keys()[2]].tolist())
dt = None
size_diff = t2.size - t1.size
if size_diff > 0:
dt = t2
r1 = np.pad(r1, (0, size_diff), 'constant')
elif size_diff < 0:
dt = t1
r2 = np.pad(r2, (0, -size_diff), 'constant')
dr = r2 - r1
plt.plot(dt, dr, label = key, linewidth=1)
plt.legend(loc='best')
plt.show()

View File

@ -1,75 +0,0 @@
import pandas as pd
import numpy as np
from numpy import diff
from scipy.interpolate import make_interp_spline
from scipy.signal import savgol_filter
import matplotlib.pyplot as plt
shots = [
{
'filename': 'Elisabeth/16_06_2022_1.xlsx',
'title': 'Elisabeth 16.06.2022 #1',
'cutoff': 38,
'data': {}
}, # [0]
]
curr_figure = 0
plt.figure(curr_figure)
plt.subplot(2, 1, 1)
# plt.title('Shotweight over time')
# plt.xlabel('Time (s)')
plt.ylabel('Weight (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 2)
# plt.title('Flow-rate over time')
plt.xlabel('Time (s)')
plt.ylabel('Flow-rate (g/s)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 1)
for shot in shots:
df_raw = pd.read_excel(shot['filename'], converters={
1: lambda x: float(x) if not str(x).endswith('.10') else float(x) + 0.85
}, sheet_name=0)
df_calc = pd.read_excel(shot['filename'], converters={
1: lambda x: float(x) if not str(x).endswith('.10') else float(x) + 0.85
}, sheet_name=1)
time_col_raw = df_raw.keys()[1]
profile_col_raw = df_raw.keys()[5]
time_col_calc = df_calc.keys()[1]
profile_col_calc = df_calc.keys()[2]
df_raw_co = df_raw.loc[df_raw[time_col_raw] < shot['cutoff']]
df_calc_co = df_calc.loc[df_calc[time_col_calc] < shot['cutoff']]
shot['data']['profile'] = df_raw_co
shot['data']['flowrate'] = df_calc_co
time = df_raw_co[time_col_raw].tolist()
weight = df_raw_co[profile_col_raw].tolist()
print("time: ", time)
print("weight: ", time)
plt.plot(time, weight, label = shot['title'], linewidth=1)
plt.subplot(2, 1, 2)
flow_time = df_calc_co[time_col_calc].tolist()
flowrate = df_calc_co[profile_col_calc].tolist()
print("flow_time: ", flow_time)
print("flowrate: ", flowrate)
plt.plot(flow_time, flowrate, label = shot['title'], linewidth=1)
plt.subplot(2, 1, 1)
plt.legend(loc='best')
plt.show()

View File

@ -1,160 +0,0 @@
import csv
import numpy as np
from numpy import diff
from scipy.interpolate import make_interp_spline
from scipy.signal import savgol_filter
import matplotlib.pyplot as plt
filenames = [
'Laura/06_09-1.csv', # [0]
'Laura/06_09-2.csv', # [1]
'Elisabeth/06_10-1.csv', #[2]
'Elisabeth/06_10-2.csv', #[3]
# 'Elisabeth/06_12-1.csv',
'Elisabeth/06_13-1.csv', #[4]
'Elisabeth/06_13-2.csv', #[5]
'Elisabeth/06_15-1.csv', #[6]
'Elisabeth/06_15-2.csv', #[7]
]
comparison = {
'Laura Shot #1 vs #2': (0, 1), #[0]
'Elisabeth 06/10/22 Shot #2 - #1': (2, 3), #[1]
'Elisabeth 06/13/22 Shot #2 - #1': (4, 5), #[2]
'Elisabeth 06/15/22 Shot #2 - #1': (6, 7), #[3]
'comparison': {
'Elisabeth Shots 06/13/22 - 06/10/22': (1, 2), #[0]
'Elisabeth Shots 06/15/22 - 06/10/22': (1, 3), #[1]
'Elisabeth Shots 06/15/22 - 06/13/22': (2, 3), #[2]
}
}
data = {}
curr_figure = 0
plt.figure(curr_figure)
plt.subplot(2, 1, 1)
# plt.title('Shotweight over time')
# plt.xlabel('Time (s)')
plt.ylabel('Weight (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 2)
# plt.title('Flow-rate over time')
plt.xlabel('Time (s)')
plt.ylabel('Flow-rate (g/s)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 1)
for filename in filenames:
data[filename] = {
'shot_name': '',
'rows': [],
'time': [],
'weight': []
}
with open(filename, newline='') as file:
reader = csv.DictReader(file, delimiter=',')
for row in reader:
data[filename]['rows'].append(row)
if row['information_type'] == 'meta':
if row['metatype'] == 'Name':
data[filename]['shot_name'] = row['metadata']
elif row['information_type'] == 'moment':
try:
elapsed = float(row['elapsed'])
weight = float(row['current_total_shot_weight'])
data[filename]['time'].append(elapsed)
data[filename]['weight'].append(weight)
except ValueError:
continue
#print("Not a float!")
time = data[filename]['time']
weight = data[filename]['weight']
first_zero = list(x > 0 for x in weight).index(True) - 1
zero_time = time[first_zero]
weight = weight[first_zero:]
time = time[first_zero:]
time = list(map(lambda t: t - zero_time, time))
data[filename]['time'] = time
data[filename]['weight'] = weight
weight_savgol = savgol_filter(weight, 30, 3)
plt.plot(time, weight_savgol, label = data[filename]['shot_name'], linewidth=1)
plt.scatter(time, weight, label = '', s=0.25)
plt.subplot(2, 1, 2)
dweight_dt = diff(weight)/diff(time)
dweight_dt_savgol = savgol_filter(dweight_dt, 60, 3)
plt.plot(time[:-1], dweight_dt_savgol, label = data[filename]['shot_name'], linewidth=1)
plt.scatter(time[1:-1], dweight_dt[1:], label = '', s=0.25)
plt.subplot(2, 1, 1)
#print(filename, "\n", time, "\n", weight)
plt.legend(loc='best')
def comparison_plot(curr_figure, comparison_dict):
curr_figure += 1
plt.figure(curr_figure)
for key in comparison_dict:
if key != 'comparison':
item = comparison_dict[key]
i0 = item[0]
i1 = item[1]
fn0 = filenames[i0]
fn1 = filenames[i1]
plt.xlabel('Time (s)')
plt.ylabel('Weight difference (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
t0 = np.array(data[fn0]['time'])
w0 = np.array(data[fn0]['weight'])
t1 = np.array(data[fn1]['time'])
w1 = np.array(data[fn1]['weight'])
time_linspace = np.linspace(max(np.array(t0).min(), np.array(t1).min()), min(np.array(t0).max(), np.array(t1).max()), max(len(t0), len(t1)))
i0 = make_interp_spline(t0, w0, k=3)
wi0 = i0(time_linspace)
i1 = make_interp_spline(t1, w1, k=3)
wi1 = i1(time_linspace)
dw = wi1 - wi0
dw_savgol = savgol_filter(dw, 30, 3)
plt.plot(time_linspace, dw_savgol, label = key, linewidth=1)
plt.scatter(time_linspace, dw, label = '', s=0.25)
plt.legend(loc='best')
else:
curr_figure = comparison_plot(curr_figure, comparison_dict['comparison'])
return curr_figure
curr_figure = comparison_plot(curr_figure, comparison)
plt.show()

1
Trunk.toml Normal file
View File

@ -0,0 +1 @@
[build]

BIN
assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/icon-1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

BIN
assets/icon-256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

28
assets/manifest.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "egui Template PWA",
"short_name": "egui-template-pwa",
"icons": [
{
"src": "./icon-256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "./maskable_icon_x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "./icon-1024.png",
"sizes": "1024x1024",
"type": "image/png"
}
],
"lang": "en-US",
"id": "/index.html",
"start_url": "./index.html",
"display": "standalone",
"background_color": "white",
"theme_color": "white"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

25
assets/sw.js Normal file
View File

@ -0,0 +1,25 @@
var cacheName = 'egui-template-pwa';
var filesToCache = [
'./',
'./index.html',
'./eframe_template.js',
'./eframe_template_bg.wasm',
];
/* Start the service worker and cache all of the app's content */
self.addEventListener('install', function (e) {
e.waitUntil(
caches.open(cacheName).then(function (cache) {
return cache.addAll(filesToCache);
})
);
});
/* Serve cached content when offline */
self.addEventListener('fetch', function (e) {
e.respondWith(
caches.match(e.request).then(function (response) {
return response || fetch(e.request);
})
);
});

View File

@ -1,16 +1,142 @@
<!doctype html>
<html lang="en">
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- Disable zooming: -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<head>
<meta charset="utf-8" />
<title>RustyBeans</title>
<meta name="viewport">
<link data-trunk rel="copy-file" href="/config.toml">
<link data-trunk rel="copy-dir" href="/Shots">
<!-- change this to your project name -->
<title>eframe template</title>
<!-- config for our rust wasm binary. go to https://trunkrs.dev/assets/#rust for more customization -->
<link data-trunk rel="rust" data-wasm-opt="2" />
<!-- this is the base url relative to which other urls will be constructed. trunk will insert this from the public-url option -->
<base data-trunk-public-url />
<link data-trunk rel="icon" href="assets/favicon.ico">
<link data-trunk rel="copy-file" href="assets/sw.js" />
<link data-trunk rel="copy-file" href="assets/manifest.json" />
<link data-trunk rel="copy-file" href="assets/icon-1024.png" />
<link data-trunk rel="copy-file" href="assets/icon-256.png" />
<link data-trunk rel="copy-file" href="assets/icon_ios_touch_192.png" />
<link data-trunk rel="copy-file" href="assets/maskable_icon_x512.png" />
<link data-trunk rel="copy-dir" href="beanconqueror" data-target-path="beanconqueror" />
<link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="icon_ios_touch_192.png">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#404040">
<style>
html {
/* Remove touch delay: */
touch-action: manipulation;
}
body {
/* Light mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #909090;
}
@media (prefers-color-scheme: dark) {
body {
/* Dark mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #404040;
}
}
/* Allow canvas to fill entire web page: */
html,
body {
overflow: hidden;
margin: 0 !important;
padding: 0 !important;
height: 100%;
width: 100%;
}
/* Position canvas in center-top: */
canvas {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 0%;
left: 50%;
transform: translate(-50%, 0%);
}
.centered {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f0f0f0;
font-size: 24px;
font-family: Ubuntu-Light, Helvetica, sans-serif;
text-align: center;
}
/* ---------------------------------------------- */
/* Loading animation from https://loading.io/css/ */
.lds-dual-ring {
display: inline-block;
width: 24px;
height: 24px;
}
.lds-dual-ring:after {
content: " ";
display: block;
width: 24px;
height: 24px;
margin: 0px;
border-radius: 50%;
border: 3px solid #fff;
border-color: #fff transparent #fff transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div id="main"></div>
<!-- The WASM code will resize the canvas dynamically -->
<!-- the id is hardcoded in main.rs . so, make sure both match. -->
<canvas id="RustyBeans"></canvas>
<!--Register Service Worker. this will cache the wasm / js scripts for offline use (for PWA functionality). -->
<!-- Force refresh (Ctrl + F5) to load the latest files instead of cached files -->
<script>
// We disable caching during development so that we always view the latest version.
if ('serviceWorker' in navigator && window.location.hash !== "#dev") {
window.addEventListener('load', function () {
navigator.serviceWorker.register('sw.js');
});
}
</script>
</body>
</html>
<!-- Powered by egui: https://github.com/emilk/egui/ -->

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

View File

@ -1,161 +0,0 @@
import pandas as pd
from datetime import datetime
import numpy as np
from numpy import diff
from scipy.interpolate import make_interp_spline
from scipy.signal import savgol_filter
import matplotlib.pyplot as plt
timestamp_start = None
def unix_to_datetime(unix_string):
return datetime.strptime(unix_string, "%H:%M:%S.%f")
def deltatime(time):
global timestamp_start
return (time - timestamp_start).total_seconds()
def timestamp_converter(timestamp):
time = unix_to_datetime(timestamp)
global timestamp_start
if timestamp_start == None:
timestamp_start = time
delta = deltatime(time)
return delta
shots = [
{
'filename': 'Elisabeth/16_06_2022_1.xlsx',
'title': 'Elisabeth 16.06.2022 #1',
'cutoff': 38,
}, # [0]
{
'filename': 'Elisabeth/19_06_2022_1.xlsx',
'title': 'Elisabeth 19.06.2022 #1',
}, # [1]
{
'filename': 'Laura/20_06_2022_1.xlsx',
'title': 'Laura 20.06.2022 #1',
'cutoff': 38.7,
}, # [2]
{
'filename': 'Laura/20_06_2022_2.xlsx',
'title': 'Laura 20.06.2022 #2',
'cutoff': 32.6,
}, # [3]
]
comparison = {
'Elisabeth 19.06.22 Shot #1 vs. #2': (0, 1), #[0]
'Laura 20.06.22 Shot #1 vs #2': (2, 3), #[1]
#'comparison': {
# 'Elisabeth Shots 06/13/22 - 06/10/22': (1, 2), #[0]
# 'Elisabeth Shots 06/15/22 - 06/10/22': (1, 3), #[1]
# 'Elisabeth Shots 06/15/22 - 06/13/22': (2, 3), #[2]
# }
}
curr_figure = 0
fig1 = plt.figure(curr_figure)
ax1 = plt.subplot(2, 2, 1)
# ax1.minorticks_on()
# plt.title('Shotweight over time')
plt.ylabel('Weight (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
ax2 = plt.subplot(2, 2, 3, sharex=ax1)
# plt.title('Flow-rate over time')
plt.ylabel('Flow-rate (g/s)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
ax3 = plt.subplot(2, 2, 2, sharex=ax1, sharey=ax1)
# plt.title('Shotweight over time')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
ax4 = plt.subplot(2, 2, 4, sharex=ax2, sharey=ax2)
# plt.title('Flow-rate over time')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
fig1.supxlabel('Time (s)')
plt.tight_layout()
plt.subplot(2, 2, 1)
for shot in shots:
timestamp_start = None
df_raw = pd.read_excel(shot['filename'], converters={
0: lambda x: timestamp_converter(x)
}, sheet_name=0)
df_calc = pd.read_excel(shot['filename'], converters={
0: lambda x: timestamp_converter(x)
}, sheet_name=1)
time_col_raw = df_raw.keys()[0]
profile_col_raw = df_raw.keys()[5]
time_col_calc = df_calc.keys()[0]
profile_col_calc = df_calc.keys()[2]
if 'cutoff' in shot:
if shot['cutoff'] != -1:
df_raw = df_raw.loc[df_raw[time_col_raw] < shot['cutoff']]
df_calc = df_calc.loc[df_calc[time_col_calc] < shot['cutoff']]
shot['data'] = {}
shot['data']['profile'] = df_raw
shot['data']['flowrate'] = df_calc
time = df_raw[time_col_raw].tolist()
weight = df_raw[profile_col_raw].tolist()
# print("time: ", time)
# print("weight: ", time)
plt.plot(time, weight, label = shot['title'], linewidth=1)
plt.subplot(2, 2, 3)
flow_time = df_calc[time_col_calc].tolist()
flowrate = df_calc[profile_col_calc].tolist()
# print("flow_time: ", flow_time)
# print("flowrate: ", flowrate)
plt.plot(flow_time, flowrate, label = shot['title'], linewidth=1)
plt.subplot(2, 2, 1)
plt.subplot(2, 2, 4)
for key in comparison:
calc1 = shots[comparison[key][0]]['data']['flowrate']
calc2 = shots[comparison[key][1]]['data']['flowrate']
t1 = np.array(calc1[calc1.keys()[0]].tolist())
r1 = np.array(calc1[calc1.keys()[2]].tolist())
t2 = np.array(calc2[calc2.keys()[0]].tolist())
r2 = np.array(calc2[calc2.keys()[2]].tolist())
dt = None
size_diff = t2.size - t1.size
if size_diff > 0:
dt = t2
r1 = np.pad(r1, (0, size_diff), 'constant')
elif size_diff < 0:
dt = t1
r2 = np.pad(r2, (0, -size_diff), 'constant')
dr = r2 - r1
plt.plot(dt, dr, label = key, linewidth=1)
plt.legend(loc='best')
plt.show()

View File

@ -1,75 +0,0 @@
import pandas as pd
import numpy as np
from numpy import diff
from scipy.interpolate import make_interp_spline
from scipy.signal import savgol_filter
import matplotlib.pyplot as plt
shots = [
{
'filename': 'Elisabeth/16_06_2022_1.xlsx',
'title': 'Elisabeth 16.06.2022 #1',
'cutoff': 38,
'data': {}
}, # [0]
]
curr_figure = 0
plt.figure(curr_figure)
plt.subplot(2, 1, 1)
# plt.title('Shotweight over time')
# plt.xlabel('Time (s)')
plt.ylabel('Weight (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 2)
# plt.title('Flow-rate over time')
plt.xlabel('Time (s)')
plt.ylabel('Flow-rate (g/s)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 1)
for shot in shots:
df_raw = pd.read_excel(shot['filename'], converters={
1: lambda x: float(x) if not str(x).endswith('.10') else float(x) + 0.85
}, sheet_name=0)
df_calc = pd.read_excel(shot['filename'], converters={
1: lambda x: float(x) if not str(x).endswith('.10') else float(x) + 0.85
}, sheet_name=1)
time_col_raw = df_raw.keys()[1]
profile_col_raw = df_raw.keys()[5]
time_col_calc = df_calc.keys()[1]
profile_col_calc = df_calc.keys()[2]
df_raw_co = df_raw.loc[df_raw[time_col_raw] < shot['cutoff']]
df_calc_co = df_calc.loc[df_calc[time_col_calc] < shot['cutoff']]
shot['data']['profile'] = df_raw_co
shot['data']['flowrate'] = df_calc_co
time = df_raw_co[time_col_raw].tolist()
weight = df_raw_co[profile_col_raw].tolist()
print("time: ", time)
print("weight: ", time)
plt.plot(time, weight, label = shot['title'], linewidth=1)
plt.subplot(2, 1, 2)
flow_time = df_calc_co[time_col_calc].tolist()
flowrate = df_calc_co[profile_col_calc].tolist()
print("flow_time: ", flow_time)
print("flowrate: ", flowrate)
plt.plot(flow_time, flowrate, label = shot['title'], linewidth=1)
plt.subplot(2, 1, 1)
plt.legend(loc='best')
plt.show()

View File

@ -1,160 +0,0 @@
import csv
import numpy as np
from numpy import diff
from scipy.interpolate import make_interp_spline
from scipy.signal import savgol_filter
import matplotlib.pyplot as plt
filenames = [
'Laura/06_09-1.csv', # [0]
'Laura/06_09-2.csv', # [1]
'Elisabeth/06_10-1.csv', #[2]
'Elisabeth/06_10-2.csv', #[3]
# 'Elisabeth/06_12-1.csv',
'Elisabeth/06_13-1.csv', #[4]
'Elisabeth/06_13-2.csv', #[5]
'Elisabeth/06_15-1.csv', #[6]
'Elisabeth/06_15-2.csv', #[7]
]
comparison = {
'Laura Shot #1 vs #2': (0, 1), #[0]
'Elisabeth 06/10/22 Shot #2 - #1': (2, 3), #[1]
'Elisabeth 06/13/22 Shot #2 - #1': (4, 5), #[2]
'Elisabeth 06/15/22 Shot #2 - #1': (6, 7), #[3]
'comparison': {
'Elisabeth Shots 06/13/22 - 06/10/22': (1, 2), #[0]
'Elisabeth Shots 06/15/22 - 06/10/22': (1, 3), #[1]
'Elisabeth Shots 06/15/22 - 06/13/22': (2, 3), #[2]
}
}
data = {}
curr_figure = 0
plt.figure(curr_figure)
plt.subplot(2, 1, 1)
# plt.title('Shotweight over time')
# plt.xlabel('Time (s)')
plt.ylabel('Weight (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 2)
# plt.title('Flow-rate over time')
plt.xlabel('Time (s)')
plt.ylabel('Flow-rate (g/s)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
plt.subplot(2, 1, 1)
for filename in filenames:
data[filename] = {
'shot_name': '',
'rows': [],
'time': [],
'weight': []
}
with open(filename, newline='') as file:
reader = csv.DictReader(file, delimiter=',')
for row in reader:
data[filename]['rows'].append(row)
if row['information_type'] == 'meta':
if row['metatype'] == 'Name':
data[filename]['shot_name'] = row['metadata']
elif row['information_type'] == 'moment':
try:
elapsed = float(row['elapsed'])
weight = float(row['current_total_shot_weight'])
data[filename]['time'].append(elapsed)
data[filename]['weight'].append(weight)
except ValueError:
continue
#print("Not a float!")
time = data[filename]['time']
weight = data[filename]['weight']
first_zero = list(x > 0 for x in weight).index(True) - 1
zero_time = time[first_zero]
weight = weight[first_zero:]
time = time[first_zero:]
time = list(map(lambda t: t - zero_time, time))
data[filename]['time'] = time
data[filename]['weight'] = weight
weight_savgol = savgol_filter(weight, 30, 3)
plt.plot(time, weight_savgol, label = data[filename]['shot_name'], linewidth=1)
plt.scatter(time, weight, label = '', s=0.25)
plt.subplot(2, 1, 2)
dweight_dt = diff(weight)/diff(time)
dweight_dt_savgol = savgol_filter(dweight_dt, 60, 3)
plt.plot(time[:-1], dweight_dt_savgol, label = data[filename]['shot_name'], linewidth=1)
plt.scatter(time[1:-1], dweight_dt[1:], label = '', s=0.25)
plt.subplot(2, 1, 1)
#print(filename, "\n", time, "\n", weight)
plt.legend(loc='best')
def comparison_plot(curr_figure, comparison_dict):
curr_figure += 1
plt.figure(curr_figure)
for key in comparison_dict:
if key != 'comparison':
item = comparison_dict[key]
i0 = item[0]
i1 = item[1]
fn0 = filenames[i0]
fn1 = filenames[i1]
plt.xlabel('Time (s)')
plt.ylabel('Weight difference (g)')
plt.grid(visible=True, which='both', axis='both', linewidth=0.5)
t0 = np.array(data[fn0]['time'])
w0 = np.array(data[fn0]['weight'])
t1 = np.array(data[fn1]['time'])
w1 = np.array(data[fn1]['weight'])
time_linspace = np.linspace(max(np.array(t0).min(), np.array(t1).min()), min(np.array(t0).max(), np.array(t1).max()), max(len(t0), len(t1)))
i0 = make_interp_spline(t0, w0, k=3)
wi0 = i0(time_linspace)
i1 = make_interp_spline(t1, w1, k=3)
wi1 = i1(time_linspace)
dw = wi1 - wi0
dw_savgol = savgol_filter(dw, 30, 3)
plt.plot(time_linspace, dw_savgol, label = key, linewidth=1)
plt.scatter(time_linspace, dw, label = '', s=0.25)
plt.legend(loc='best')
else:
curr_figure = comparison_plot(curr_figure, comparison_dict['comparison'])
return curr_figure
curr_figure = comparison_plot(curr_figure, comparison)
plt.show()

View File

@ -55,6 +55,11 @@ impl Config {
toml::from_str(&config_file).expect("Can't deserialize config.toml")
}
pub fn from_raw(bytes: &Vec<u8>) -> Self {
let config_file = core::str::from_utf8(&bytes).expect("Not valid UTF-8");
toml::from_str(&config_file).expect("Can't deserialize config.toml")
}
pub fn from_default() -> Self {
Self::from_file("config.toml")
}

View File

@ -71,7 +71,7 @@ impl Database {
}
pub fn from_config(config: &config::Config) -> Self {
Database::from_file(&format!("{}/{}", &config.data_dir, &config.main_json))
Database::from_file(&format!("./{}/{}", &config.data_dir, &config.main_json))
}
pub fn bean_names(&self) -> Vec<String> {

View File

@ -8,20 +8,14 @@ mod ui;
use ui::Ui;
use crate::plot::database_plot_selected_tui;
use crate::plot::generate_plots;
extern crate console_error_panic_hook;
use std::panic;
use eframe::egui;
fn main() -> Result<(), eframe::Error> {
panic::set_hook(Box::new(console_error_panic_hook::hook));
#[cfg(not(target_arch = "wasm32"))]
fn main() -> eframe::Result<()> {
env_logger::init();
// generate_plots();
// database_plot_selected_tui();
let options = eframe::NativeOptions {
drag_and_drop_support: true,
min_window_size: Some(egui::vec2(640.0, 360.0)),
@ -34,3 +28,21 @@ fn main() -> Result<(), eframe::Error> {
Box::new(|_cc| Box::new(Ui::default())),
)
}
#[cfg(target_arch = "wasm32")]
fn main() {
eframe::WebLogger::init(log::LevelFilter::Debug).ok();
let options = eframe::WebOptions::default();
wasm_bindgen_futures::spawn_local(async {
eframe::WebRunner::new()
.start(
"RustyBeans",
options,
Box::new(|cc| Box::new(Ui::default())),
)
.await
.expect("Failed to start eframe");
});
}

View File

@ -4,17 +4,7 @@ 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::widgets::plot::{PlotPoint, PlotPoints};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use notify_rust::Notification;
use plotly::{
common::{Mode, Title},
Layout, Plot, Scatter,
};
use egui::plot::{PlotPoint, PlotPoints};
use std::collections::HashMap;
use std::fs;
@ -29,72 +19,7 @@ pub struct LoadingProgress {
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");
let config: Config = toml::from_str(&config_file).expect("Can't deserialize config.toml");
let mut result: Vec<(String, Plot)> = Vec::with_capacity(config.charts.len());
for chart in config.charts {
// println!("Chart: {}\n", chart.1.title);
let filename = format!("{}/{}.html", &config.output_dir, &chart.1.title);
let mut plot = Plot::new();
let _shot_count = chart.1.shots.len();
for shot_nr in chart.1.shots {
if let Some(shot) = config.shots.get(&shot_nr.to_string()) {
// println!("\tShot: {}n", shot.title);
if let Some(shot_json) = &shot.json {
let brew = FlowProfile::from_file(
&format!("{}/{}_flow_profile.json", config.brew_dir, shot_json),
shot.cutoff,
)
.preprocess_json();
let (x, y): (Vec<_>, Vec<_>) = brew
.data_collection
.unwrap_or_else(|| {
panic!("No data_collection present for shot_json: {}", shot_json)
})
.weight
.iter()
.cloned()
.unzip();
let trace = Scatter::new(x, y).name(&shot.title).mode(Mode::Lines);
plot.add_trace(trace);
} else if let Some(shot_filename) = &shot.filename {
if let Some(data) = load_data(
&format!("{}/{}", config.shot_dir, shot_filename),
shot.cutoff,
) {
if let Some(disable) = shot.disable {
if disable {
continue;
}
}
let (x, y): (Vec<_>, Vec<_>) = data.weight.into_iter().unzip();
let trace = Scatter::new(x, y).name(&shot.title).mode(Mode::Lines);
plot.add_trace(trace);
}
}
}
}
let layout = Layout::new().title(Title::new(&chart.1.title));
plot.set_layout(layout);
plot.use_local_plotly();
plot.write_html(filename);
result.push((chart.1.title, plot));
}
result
pub finished: bool,
}
pub fn plot_points_to_owned(plot_points: &RawPlotPoints) -> PlotPoints {
@ -135,12 +60,17 @@ pub fn database_plot_entries(
let mut plot_entries: HashMap<String, PlotEntry> = HashMap::new();
let step_size = 1.0 / brews.len() as f32;
if let Some(progress) = &progress {
let mut progress_lock = progress.lock().unwrap();
(*progress_lock).total = brews.len();
(*progress_lock).finished = false;
}
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 + 1;
(*progress_lock).total = brews.len();
(*progress_lock).percentage += step_size;
}
@ -149,193 +79,10 @@ pub fn database_plot_entries(
}
}
if let Some(progress) = &progress {
let mut progress_lock = progress.lock().unwrap();
(*progress_lock).finished = true;
}
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());
let database = Database::from_config(&config);
let bean_names = database.bean_names();
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.to_owned();
let title = format!("{} from {}", &bean.name, unix_to_human_date(bean_timestamp));
let filename = format!(
"{}/{}_from_{}.html",
&config.output_dir,
&bean.name,
unix_to_machine_date(bean_timestamp)
)
.replace(" ", "_");
let mut plot = Plot::new();
let brews = &database.brews_for_bean(&bean);
for brew in brews {
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.to_owned());
let brew = FlowProfile::from_file(
&format!("{}/{}", &config.data_dir, &flow_profile),
None,
)
.preprocess_json();
let (x, y): (Vec<_>, Vec<_>) = brew
.data_collection
.unwrap_or_else(|| {
panic!(
"No data_collection present for flow_profile: {}",
&flow_profile
)
})
.weight
.iter()
.cloned()
.unzip();
let trace = Scatter::new(x, y).name(&brew_title).mode(Mode::Lines);
plot.add_trace(trace);
// progress_brews.inc(..);
}
}
}
let layout = Layout::new().title(Title::new(&title));
plot.set_layout(layout);
plot.use_local_plotly();
plot.write_html(filename);
result.push((title.to_owned(), plot));
}
// progress_beans.inc(..);
}
}
Notification::new()
.summary("RustyBeans finished")
.body("Successfully generated all selected bean charts automatically, according to the database.")
.timeout(5000)
.show()
.expect("Couldn't show desktop notification");
result
}
pub fn database_plot_selected_tui() -> Vec<(String, Plot)> {
let config = Config::from_default();
let mut result: Vec<(String, Plot)> = Vec::with_capacity(config.charts.len());
let database = Database::from_config(&config);
let bean_names = database.bean_names();
let selection = MultiSelect::with_theme(&ColorfulTheme::default())
.with_prompt("Select the Beans you want to automatically generate charts for:")
.items(&bean_names[..])
.interact()
.expect("You need to select at least one bean to proceed");
if !selection.is_empty() {
let multi_progress = MultiProgress::new();
multi_progress.println("Generating brew charts.\nCheck the specified output directory and open the corresponding .html files to view.").unwrap();
let progress_style = ProgressStyle::with_template(
"[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
)
.expect("Can't generate progress bar style");
let progress_beans = multi_progress.add(
ProgressBar::new(selection.len() as u64)
.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.to_owned();
let title = format!("{} from {}", &bean.name, unix_to_human_date(bean_timestamp));
let filename = format!(
"{}/{}_from_{}.html",
&config.output_dir,
&bean.name,
unix_to_machine_date(bean_timestamp)
)
.replace(" ", "_");
let mut plot = Plot::new();
let brews = &database.brews_for_bean(&bean);
progress_brews = multi_progress.insert_after(
&progress_beans,
ProgressBar::new(brews.len() as u64)
.with_style(progress_style.to_owned())
.with_message("Brews"),
);
for brew in brews {
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.to_owned());
let brew = FlowProfile::from_file(
&format!("{}/{}", &config.data_dir, &flow_profile),
None,
)
.preprocess_json();
let (x, y): (Vec<_>, Vec<_>) = brew
.data_collection
.unwrap_or_else(|| {
panic!(
"No data_collection present for flow_profile: {}",
&flow_profile
)
})
.weight
.iter()
.cloned()
.unzip();
let trace = Scatter::new(x, y).name(&brew_title).mode(Mode::Lines);
plot.add_trace(trace);
progress_brews.inc(1);
}
}
}
let layout = Layout::new().title(Title::new(&title));
plot.set_layout(layout);
plot.use_local_plotly();
plot.write_html(filename);
result.push((title.to_owned(), plot));
}
progress_beans.inc(1);
multi_progress.remove(&progress_brews);
}
}
Notification::new()
.summary("RustyBeans finished")
.body("Successfully generated all selected bean charts automatically, according to the database.")
.timeout(5000)
.show()
.expect("Couldn't show desktop notification");
result
}

151
src/ui.rs
View File

@ -2,12 +2,8 @@
use eframe::egui;
use egui::{
widgets::plot::{Legend, Line, Plot},
Align, Layout, ProgressBar,
};
use plotly::layout::Center;
use rfd::FileDialog;
use egui::{plot::{Legend, Line, Plot}, Align, Layout, ProgressBar, Modifiers};
use rfd::{FileHandle, AsyncFileDialog};
use crate::{
config::Config,
@ -24,6 +20,8 @@ use std::{
sync::{Arc, Mutex},
};
use async_std::task;
#[derive(Default, Debug)]
struct LoadingData {
config: Config,
@ -40,9 +38,9 @@ pub struct Ui {
side_panel_expanded: bool,
loading_progress: Arc<Mutex<LoadingProgress>>,
loader_thread: Option<JoinHandle<()>>,
loading_data: Arc<Mutex<Option<LoadingData>>>,
data_loaded: bool,
loading_file: Arc<Mutex<Option<Vec<u8>>>>,
data_transfered: bool,
config: Config,
database: Database,
@ -53,8 +51,6 @@ pub struct Ui {
select_all: bool,
clear_all: bool,
picked_path: Option<String>,
plot_entries: HashMap<String, PlotEntry>,
continuous_mode: bool,
@ -79,16 +75,12 @@ impl Ui {
.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;
pub fn reload(&mut self, _ctx: &egui::Context) {
if self.data_transfered {
self.data_transfered = false;
let loading_progress_arc = self.loading_progress.clone();
let mut loading_progress_lock = loading_progress_arc.lock().unwrap();
*loading_progress_lock = LoadingProgress::default();
}
let mut loading_progress_lock = self.loading_progress.lock().unwrap();
*loading_progress_lock = LoadingProgress::default();
}
}
}
@ -113,15 +105,15 @@ impl eframe::App for Ui {
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!");
});
}
}
}
// 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);
@ -135,8 +127,7 @@ impl eframe::App for Ui {
egui::CentralPanel::default().show(ctx, |ui| {
ui.centered_and_justified(|ui| {
if self.show_loading_screen {
let loading_progress_arc = self.loading_progress.clone();
let loading_progress_lock = loading_progress_arc.lock().unwrap();
let loading_progress_lock = self.loading_progress.lock().unwrap();
ui.add(
ProgressBar::new(loading_progress_lock.percentage.to_owned()).text(
@ -149,15 +140,14 @@ impl eframe::App for Ui {
),
);
if let Some(loader_thread) = &self.loader_thread {
if loader_thread.is_finished() {
self.continuous_mode = false;
self.modal = false;
self.show_loading_screen = false;
}
if loading_progress_lock.finished {
self.continuous_mode = false;
self.modal = false;
self.show_loading_screen = false;
}
}
#[cfg(not(target_arch = "wasm32"))]
if self.show_confirmation_dialog {
// Show confirmation dialog:
ui.separator();
@ -187,7 +177,7 @@ impl eframe::App for Ui {
ui.horizontal_centered(|ui| {
ui.vertical(|ui| {
if self.data_loaded {
if self.data_transfered {
ui.heading("Beans");
egui::ScrollArea::vertical()
@ -204,7 +194,7 @@ impl eframe::App for Ui {
)
.clicked_by(egui::PointerButton::Primary)
{
if ctx.input(|i| i.modifiers.ctrl) {
if ctx.input(|i| i.modifiers.command) {
self.select_all = true;
}
}
@ -215,7 +205,7 @@ impl eframe::App for Ui {
});
ui.vertical(|ui| {
if self.data_loaded {
if self.data_transfered {
if let Some(bean) =
&self.database.bean_for_uuid(&self.selected_bean)
{
@ -253,7 +243,7 @@ impl eframe::App for Ui {
ui.horizontal_centered(|ui| {
ui.vertical(|ui| {
if self.data_loaded {
if self.data_transfered {
ui.heading("Selected Brews");
egui::ScrollArea::vertical()
@ -286,7 +276,7 @@ impl eframe::App for Ui {
egui::PointerButton::Primary,
)
{
if ctx.input(|i| i.modifiers.ctrl) {
if ctx.input(|i| i.modifiers.matches(Modifiers::CTRL)) {
self.clear_all = true;
}
}
@ -322,25 +312,34 @@ impl eframe::App for Ui {
);
ui.with_layout(
Layout::top_down(Align::Center).with_main_align(egui::Align::Center),
Layout::top_down(Align::Center).with_main_align(Align::Center),
|ui| {
if let Some(picked_path) = &self.picked_path {
let thread_handle = self.loader_thread.get_or_insert_with(|| {
let file_loaded: bool;
{
file_loaded = self.loading_file.lock().unwrap().is_some();
}
if file_loaded {
let loading_finished: bool;
{
loading_finished = self.loading_progress.lock().unwrap().finished;
}
if !loading_finished {
self.continuous_mode = true;
self.modal = true;
self.show_loading_screen = true;
let loading_file_arc = self.loading_file.clone();
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();
thread::spawn(move || {
let data_loader = async move {
let ctx = ctx_clone;
let mut loading_data = LoadingData::default();
loading_data.config = Config::from_file(&picked_path_owned);
let loading_file_lock = loading_file_arc.lock().unwrap();
loading_data.config = Config::from_raw(loading_file_lock.as_ref().expect("No file loaded"));
loading_data.database =
Database::from_config(&loading_data.config);
@ -378,11 +377,15 @@ impl eframe::App for Ui {
*loading_data_lock = Some(loading_data);
ctx.request_repaint();
})
});
};
if thread_handle.is_finished() {
if !self.data_loaded {
#[cfg(not(target_arch = "wasm32"))]
task::spawn(data_loader);
#[cfg(target_arch = "wasm32")]
wasm_bindgen_futures::spawn_local(data_loader);
} else {
if !self.data_transfered {
self.side_panel_expanded = true;
// println!("loading_data: {:?}", &self.loading_data);
@ -397,7 +400,7 @@ impl eframe::App for Ui {
self.selected_bean = loading_data.selected_bean;
self.plot_entries = loading_data.plot_entries;
self.data_loaded = true;
self.data_transfered = true;
} else {
Plot::new("Combined Chart of selected brews")
.legend(Legend::default())
@ -420,26 +423,33 @@ impl eframe::App for Ui {
}
}
} else {
ui.label("Select the config.toml to start");
ui.label("Select the config file (.toml) to start");
if ui.button("Open file").clicked() {
if let Some(path) = FileDialog::new()
.add_filter("toml", &["toml"])
.set_directory(
match &env::current_dir() {
Ok(path) => path.to_str(),
Err(_) => None,
}
.unwrap_or_default(),
)
.pick_file()
{
self.picked_path = Some(path.display().to_string());
}
}
if ui.button("Load file").clicked() {
let loading_file_arc = self.loading_file.clone();
if ui.button("Open default config.toml").clicked() {
self.picked_path = Some(String::from("config.toml"));
let file_dialog = async move {
let file = AsyncFileDialog::new()
.add_filter("toml", &["toml"])
.set_directory(
match &env::current_dir() {
Ok(path) => path.to_str(),
Err(_) => None,
}.unwrap_or_default()
)
.pick_file().await;
let data = file.unwrap().read().await;
let mut loading_file_lock = loading_file_arc.lock().unwrap();
*loading_file_lock = Some(data);
};
#[cfg(not(target_arch = "wasm32"))]
task::spawn(file_dialog);
#[cfg(target_arch = "wasm32")]
wasm_bindgen_futures::spawn_local(file_dialog);
}
}
},
@ -449,6 +459,7 @@ impl eframe::App for Ui {
}
}
#[cfg(not(target_arch = "wasm32"))]
fn on_close_event(&mut self) -> bool {
self.modal = true;
self.show_confirmation_dialog = true;