add time-range and auto-refresh
This commit is contained in:
160
app.py
160
app.py
@@ -14,17 +14,28 @@ try:
|
|||||||
import plotly.io as pio
|
import plotly.io as pio
|
||||||
import plotly as ply
|
import plotly as ply
|
||||||
import plotly.subplots
|
import plotly.subplots
|
||||||
|
import local_file_picker
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
print('Module `plotly` not found.')
|
print('Module `plotly` not found.')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
NAME = 'tempplot'
|
pio.templates["plotly_light"] = pgo.layout.Template(
|
||||||
FILENAME = './data/20260420213656.txt'
|
layout = pio.templates["plotly_white"].layout)
|
||||||
fig = None
|
#pio.templates["plotly_light"].layout['scene']['xaxis']['gridcolor'] = 'gray'
|
||||||
plot = None
|
pio.templates["plotly_light"].layout['xaxis']['gridcolor'] = 'lightgray'
|
||||||
label_file = None
|
pio.templates["plotly_light"].layout['xaxis']['zerolinecolor'] = 'lightgray'
|
||||||
label_file_fmt = 'tempplot: {filename}'
|
pio.templates["plotly_light"].layout['yaxis']['gridcolor'] = 'lightgray'
|
||||||
# label_file_fmt = 'tempplot: {os.path.basename(filename)}'
|
pio.templates["plotly_light"].layout['yaxis']['zerolinecolor'] = 'lightgray'
|
||||||
|
|
||||||
|
ELM = {
|
||||||
|
'name': 'tempplot',
|
||||||
|
'filename': './data/20260420213656.txt',
|
||||||
|
'label_file_fmt': 'file: {filename}',
|
||||||
|
#'label_data_len_fmt': 'data points (rendered): {num}',
|
||||||
|
'label_data_len_fmt': 'data points: {num} (rendered), {numa} (total)',
|
||||||
|
'plotly_light': 'plotly_light',
|
||||||
|
'plotly_dark': 'plotly_dark',
|
||||||
|
}
|
||||||
|
|
||||||
def main_cli():
|
def main_cli():
|
||||||
import argparse
|
import argparse
|
||||||
@@ -67,19 +78,16 @@ def np2pgo(x, y, label=None):
|
|||||||
res.update({'name': label})
|
res.update({'name': label})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# def str2date(x):
|
|
||||||
# return datetime.datetime.strptime(x.decode('utf-8'), '%Y%m%d%H%M%S')
|
|
||||||
|
|
||||||
import local_file_picker
|
|
||||||
async def pick_file() -> None:
|
async def pick_file() -> None:
|
||||||
result = await local_file_picker.local_file_picker('./data', multiple=False)
|
result = await local_file_picker.local_file_picker('./data', multiple=False)
|
||||||
# ui.notify(f'You chose {result}')
|
# ui.notify(f'You chose {result}')
|
||||||
filename = result[-1]
|
if result:
|
||||||
update_data(filename)
|
filename = result[-1]
|
||||||
label_file.set_text(label_file_fmt.format(filename=os.path.basename(filename)))
|
ELM['filename'] = filename
|
||||||
|
# update_data() # see timer below
|
||||||
|
ELM['label_file'].set_text(ELM['label_file_fmt'].format(filename=os.path.basename(filename)))
|
||||||
|
|
||||||
def get_data(filename=FILENAME):
|
async def get_data(filename=ELM['filename']):
|
||||||
print(f"filename: {filename}")
|
|
||||||
data = np.genfromtxt(fname=filename, skip_header=1)
|
data = np.genfromtxt(fname=filename, skip_header=1)
|
||||||
|
|
||||||
if len(data) > 0 and len(data[0]) > 6:
|
if len(data) > 0 and len(data[0]) > 6:
|
||||||
@@ -92,93 +100,97 @@ def get_data(filename=FILENAME):
|
|||||||
0: lambda x: datetime.datetime.strptime(x, '%Y%m%d%H%M%S')},
|
0: lambda x: datetime.datetime.strptime(x, '%Y%m%d%H%M%S')},
|
||||||
names=names, encoding='utf-8'
|
names=names, encoding='utf-8'
|
||||||
)
|
)
|
||||||
print(data.shape)
|
|
||||||
print(data['time'])
|
|
||||||
#data[:, 0] = np.array(data[:, 0], dtype='datetime64')
|
|
||||||
#print(data.time)
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def update_data(filename=FILENAME):
|
async def update_data():
|
||||||
data = get_data(filename)
|
filename=ELM['filename']
|
||||||
|
data_all = await get_data(filename)
|
||||||
|
data = data_all
|
||||||
|
# time_range = ELM['number_time_range'].value
|
||||||
|
time_range = app.storage.user['time_range']
|
||||||
|
if time_range is not None and time_range > 0:
|
||||||
|
# delta = datetime.datetime.now() - data['time']
|
||||||
|
delta = data['time'][-1] - data['time']
|
||||||
|
data = data[np.where(delta <= datetime.timedelta(hours=time_range))]
|
||||||
|
print(filename, data_all.shape, data.shape)
|
||||||
|
ELM['label_data_len'].set_text(ELM['label_data_len_fmt'].format(num=len(data), numa=len(data_all)))
|
||||||
|
|
||||||
fig.data = []
|
ELM['fig'].data = []
|
||||||
plot.visible = False
|
ELM['plot'].visible = False
|
||||||
plot.update()
|
ELM['plot'].update()
|
||||||
|
|
||||||
fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['ts'], 'Setp')))
|
ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['ts'], 'Setp')))
|
||||||
fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['ta'], 'Temp A')))
|
ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['ta'], 'Temp A')))
|
||||||
fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['tb'], 'Temp B')))
|
ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['tb'], 'Temp B')))
|
||||||
if len(data) > 0 and len(data[0]) > 6:
|
if len(data) > 0 and len(data[0]) > 6:
|
||||||
fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['tc'], 'Temp C')))
|
ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['tc'], 'Temp C')))
|
||||||
fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['td'], 'Temp D')))
|
ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['td'], 'Temp D')))
|
||||||
|
|
||||||
fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['hp'], 'Heat')),
|
ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['hp'], 'Heat')),
|
||||||
row=2, col=1)
|
row=2, col=1)
|
||||||
|
|
||||||
plot.visible = True
|
ELM['plot'].visible = True
|
||||||
plot.update()
|
ELM['plot'].update()
|
||||||
|
|
||||||
@ui.page('/')
|
@ui.page('/')
|
||||||
def index():
|
async def index():
|
||||||
global fig, plot, label_file
|
await ui.context.client.connected()
|
||||||
|
|
||||||
# data = get_data()
|
# initialize objects
|
||||||
|
ELM['fig'] = ply.subplots.make_subplots(rows=2, cols=1)
|
||||||
|
|
||||||
#
|
# render header
|
||||||
fig = ply.subplots.make_subplots(rows=2, cols=1)
|
|
||||||
|
|
||||||
#
|
|
||||||
ui.context.client.content.classes('h-[100vh]')
|
ui.context.client.content.classes('h-[100vh]')
|
||||||
ui.add_head_html('''<meta name="viewport", content="width=device-width, initial-scale=1">
|
ui.add_head_html('''<meta name="viewport", content="width=device-width, initial-scale=1">
|
||||||
<meta name="mobile-web-app-capable" content="yes">''')
|
<meta name="mobile-web-app-capable" content="yes">''')
|
||||||
|
|
||||||
dark_mode = ui.dark_mode().bind_value(app.storage.user, 'dark_mode')
|
dark_mode = ui.dark_mode().bind_value(app.storage.user, 'dark_mode')
|
||||||
#ui.button('Dark', on_click=dark.enable)
|
|
||||||
#ui.button('Light', on_click=dark.disable)
|
|
||||||
ui.switch(
|
|
||||||
'Dark mode', on_change=lambda: fig.update_layout(template='plotly_dark' if dark_mode.value else 'plotly_white')
|
|
||||||
).bind_value(app.storage.user, 'dark_mode').props('icon="dark_mode"')
|
|
||||||
print(dark_mode.value)
|
|
||||||
|
|
||||||
ui.colors(primary='#4888c4')
|
ui.colors(primary='#4888c4')
|
||||||
|
|
||||||
ui.button('Choose file', on_click=pick_file, icon='folder')
|
|
||||||
|
|
||||||
label_file = ui.label(label_file_fmt.format(filename=os.path.basename(FILENAME)))
|
with ui.row(align_items="center").classes('w-full'):
|
||||||
|
ui.button('Choose file', on_click=pick_file, icon='folder')
|
||||||
|
|
||||||
# range_t = ui.range(
|
ELM['number_time_range'] = ui.number(
|
||||||
# min=data['time'][0].timestamp(), max=data['time'][-1].timestamp(),
|
label='time range', value=6, format='%.2f', min=0, step=0.5,
|
||||||
# # on_change=lambda e: fig.update_xaxes(
|
suffix='h',
|
||||||
# # range=[str(datetime.datetime.fromtimestamp(e.value["min"])),
|
# on_change=lambda e: update_data(), # see timer below
|
||||||
# # str(datetime.datetime.fromtimestamp(e.value["max"]))]),
|
).props('clearable').bind_value(app.storage.user, 'time_range').classes('w-32')
|
||||||
# )
|
|
||||||
# ui.label().bind_text_from(
|
ELM['switch_timer'] = ui.switch('auto-refresh').bind_value(app.storage.user, 'timer')
|
||||||
# range_t, 'value',
|
|
||||||
# backward=lambda v: (
|
ui.space()
|
||||||
# f'min: {datetime.datetime.fromtimestamp(v["min"])}\n\
|
|
||||||
# max: {datetime.datetime.fromtimestamp(v["max"])}'),
|
ui.switch(
|
||||||
# ).style('white-space: pre-wrap')
|
'Dark mode', on_change=lambda: (
|
||||||
|
ELM['fig'].update_layout(
|
||||||
|
template=ELM['plotly_dark'] if dark_mode.value else ELM['plotly_light']),
|
||||||
|
'plot' in ELM and ELM['plot'].update())
|
||||||
|
).bind_value(app.storage.user, 'dark_mode').props('icon="dark_mode"')
|
||||||
|
|
||||||
|
with ui.row():
|
||||||
|
ELM['label_file'] = ui.label(
|
||||||
|
ELM['label_file_fmt'].format(filename=os.path.basename(ELM['filename'])))
|
||||||
|
ELM['label_data_len'] = ui.label(ELM['label_data_len_fmt'].format(num=0, numa=0))
|
||||||
|
|
||||||
pio.templates.default = 'plotly_dark'
|
pio.templates.default = 'plotly_dark'
|
||||||
# fig = pgo.Figure()
|
# ELM['fig'] = pgo.Figure()
|
||||||
fig.update_layout(
|
ELM['fig'].update_layout(
|
||||||
height=600,
|
height=600,
|
||||||
#template='plotly_white'
|
#template='plotly_white'
|
||||||
template='plotly_dark' if dark_mode.value else 'plotly_white',
|
template=ELM['plotly_dark'] if dark_mode.value else ELM['plotly_light'],
|
||||||
)
|
)
|
||||||
plot = ui.plotly(fig).classes('w-full')
|
ELM['plot'] = ui.plotly(ELM['fig']).classes('w-full')
|
||||||
|
|
||||||
update_data()
|
# update_data()
|
||||||
|
ui.timer(0, lambda: update_data(), once=True)
|
||||||
# print(fig.full_figure_for_development().layout.xaxis.range)
|
ELM['timer'] = ui.timer(2.0, lambda: update_data(), immediate=False)
|
||||||
# ('2026-04-20 21:36:56', '2026-04-21 10:40:53')
|
ELM['switch_timer'].bind_value_to(ELM['timer'], 'active')
|
||||||
# print(fig.full_figure_for_development().layout.xaxis.range[0])
|
|
||||||
# print(type(fig.full_figure_for_development().layout.xaxis.range[0]))
|
|
||||||
|
|
||||||
def main_gui(args):
|
def main_gui(args):
|
||||||
ui.run(
|
ui.run(
|
||||||
host=args.host, port=int(args.port), title=NAME,
|
host=args.host, port=int(args.port), title=ELM['name'],
|
||||||
#dark=True,
|
dark=False,
|
||||||
native=False,
|
native=False,
|
||||||
show=False, storage_secret='tempview')
|
show=False, storage_secret='tempview')
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# https://github.com/zauberzeug/nicegui/blob/main/examples/local_file_picker/local_file_picker.py
|
# based on https://github.com/zauberzeug/nicegui/blob/main/examples/local_file_picker/local_file_picker.py
|
||||||
import platform
|
import platform
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -30,9 +30,19 @@ class local_file_picker(ui.dialog):
|
|||||||
with self, ui.card():
|
with self, ui.card():
|
||||||
self.add_drives_toggle()
|
self.add_drives_toggle()
|
||||||
self.grid = ui.aggrid({
|
self.grid = ui.aggrid({
|
||||||
'columnDefs': [{'field': 'name', 'headerName': 'File'}],
|
'columnDefs': [{
|
||||||
'rowSelection': {'mode': 'multiRow' if multiple else 'singleRow'},
|
'field': 'name', 'headerName': 'File', 'sort': 'desc',
|
||||||
}, html_columns=[0]).classes('w-96').on('cellDoubleClicked', self.handle_double_click)
|
'filter': 'agTextColumnFilter', 'floatingFilter': True,
|
||||||
|
}],
|
||||||
|
'rowSelection': {
|
||||||
|
'mode': 'multiRow' if multiple else 'singleRow',
|
||||||
|
#'checkboxes': True if multiple else False,
|
||||||
|
'enableClickSelection': True,
|
||||||
|
},
|
||||||
|
'selectionColumnDef': {'width': 28, 'cellStyle': {'textAlign': 'center'} }, # will be width+20 and min 48
|
||||||
|
}, html_columns=[0], theme='balham').classes('w-96').on('cellDoubleClicked', self.handle_double_click)
|
||||||
|
#\
|
||||||
|
#.on('cellClicked', self.handle_click)
|
||||||
with ui.row().classes('w-full justify-end'):
|
with ui.row().classes('w-full justify-end'):
|
||||||
ui.button('Cancel', on_click=self.close).props('outline')
|
ui.button('Cancel', on_click=self.close).props('outline')
|
||||||
ui.button('Ok', on_click=self._handle_ok)
|
ui.button('Ok', on_click=self._handle_ok)
|
||||||
|
|||||||
Reference in New Issue
Block a user