diff --git a/app.py b/app.py
index 9b06295..caa2cdc 100644
--- a/app.py
+++ b/app.py
@@ -14,17 +14,28 @@ try:
import plotly.io as pio
import plotly as ply
import plotly.subplots
+ import local_file_picker
except ModuleNotFoundError as e:
print('Module `plotly` not found.')
exit(1)
-NAME = 'tempplot'
-FILENAME = './data/20260420213656.txt'
-fig = None
-plot = None
-label_file = None
-label_file_fmt = 'tempplot: {filename}'
-# label_file_fmt = 'tempplot: {os.path.basename(filename)}'
+pio.templates["plotly_light"] = pgo.layout.Template(
+ layout = pio.templates["plotly_white"].layout)
+#pio.templates["plotly_light"].layout['scene']['xaxis']['gridcolor'] = 'gray'
+pio.templates["plotly_light"].layout['xaxis']['gridcolor'] = 'lightgray'
+pio.templates["plotly_light"].layout['xaxis']['zerolinecolor'] = 'lightgray'
+pio.templates["plotly_light"].layout['yaxis']['gridcolor'] = 'lightgray'
+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():
import argparse
@@ -67,19 +78,16 @@ def np2pgo(x, y, label=None):
res.update({'name': label})
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:
result = await local_file_picker.local_file_picker('./data', multiple=False)
# ui.notify(f'You chose {result}')
- filename = result[-1]
- update_data(filename)
- label_file.set_text(label_file_fmt.format(filename=os.path.basename(filename)))
+ if result:
+ filename = result[-1]
+ 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):
- print(f"filename: {filename}")
+async def get_data(filename=ELM['filename']):
data = np.genfromtxt(fname=filename, skip_header=1)
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')},
names=names, encoding='utf-8'
)
- print(data.shape)
- print(data['time'])
- #data[:, 0] = np.array(data[:, 0], dtype='datetime64')
- #print(data.time)
return data
-def update_data(filename=FILENAME):
- data = get_data(filename)
+async def update_data():
+ 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 = []
- plot.visible = False
- plot.update()
+ ELM['fig'].data = []
+ ELM['plot'].visible = False
+ ELM['plot'].update()
- fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['ts'], 'Setp')))
- 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['ts'], 'Setp')))
+ ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['ta'], 'Temp A')))
+ ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['tb'], 'Temp B')))
if len(data) > 0 and len(data[0]) > 6:
- 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['tc'], 'Temp C')))
+ ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['td'], 'Temp D')))
- fig.add_trace(pgo.Scatter(**np2pgo(data['time'], data['hp'], 'Heat')),
- row=2, col=1)
+ ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['hp'], 'Heat')),
+ row=2, col=1)
- plot.visible = True
- plot.update()
+ ELM['plot'].visible = True
+ ELM['plot'].update()
@ui.page('/')
-def index():
- global fig, plot, label_file
+async def index():
+ await ui.context.client.connected()
- # data = get_data()
+ # initialize objects
+ ELM['fig'] = ply.subplots.make_subplots(rows=2, cols=1)
- #
- fig = ply.subplots.make_subplots(rows=2, cols=1)
-
- #
+ # render header
ui.context.client.content.classes('h-[100vh]')
ui.add_head_html('''
''')
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.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(
-# min=data['time'][0].timestamp(), max=data['time'][-1].timestamp(),
-# # on_change=lambda e: fig.update_xaxes(
-# # range=[str(datetime.datetime.fromtimestamp(e.value["min"])),
-# # str(datetime.datetime.fromtimestamp(e.value["max"]))]),
-# )
-# ui.label().bind_text_from(
-# range_t, 'value',
-# backward=lambda v: (
-# f'min: {datetime.datetime.fromtimestamp(v["min"])}\n\
-# max: {datetime.datetime.fromtimestamp(v["max"])}'),
-# ).style('white-space: pre-wrap')
+ ELM['number_time_range'] = ui.number(
+ label='time range', value=6, format='%.2f', min=0, step=0.5,
+ suffix='h',
+ # on_change=lambda e: update_data(), # see timer below
+ ).props('clearable').bind_value(app.storage.user, 'time_range').classes('w-32')
+
+ ELM['switch_timer'] = ui.switch('auto-refresh').bind_value(app.storage.user, 'timer')
+
+ ui.space()
+
+ ui.switch(
+ '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'
- # fig = pgo.Figure()
- fig.update_layout(
+ # ELM['fig'] = pgo.Figure()
+ ELM['fig'].update_layout(
height=600,
#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()
-
- # print(fig.full_figure_for_development().layout.xaxis.range)
- # ('2026-04-20 21:36:56', '2026-04-21 10:40:53')
- # print(fig.full_figure_for_development().layout.xaxis.range[0])
- # print(type(fig.full_figure_for_development().layout.xaxis.range[0]))
+ # update_data()
+ ui.timer(0, lambda: update_data(), once=True)
+ ELM['timer'] = ui.timer(2.0, lambda: update_data(), immediate=False)
+ ELM['switch_timer'].bind_value_to(ELM['timer'], 'active')
def main_gui(args):
ui.run(
- host=args.host, port=int(args.port), title=NAME,
- #dark=True,
+ host=args.host, port=int(args.port), title=ELM['name'],
+ dark=False,
native=False,
show=False, storage_secret='tempview')
diff --git a/local_file_picker.py b/local_file_picker.py
index 00fb505..e5288e0 100644
--- a/local_file_picker.py
+++ b/local_file_picker.py
@@ -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
from pathlib import Path
@@ -30,9 +30,19 @@ class local_file_picker(ui.dialog):
with self, ui.card():
self.add_drives_toggle()
self.grid = ui.aggrid({
- 'columnDefs': [{'field': 'name', 'headerName': 'File'}],
- 'rowSelection': {'mode': 'multiRow' if multiple else 'singleRow'},
- }, html_columns=[0]).classes('w-96').on('cellDoubleClicked', self.handle_double_click)
+ 'columnDefs': [{
+ 'field': 'name', 'headerName': 'File', 'sort': 'desc',
+ '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'):
ui.button('Cancel', on_click=self.close).props('outline')
ui.button('Ok', on_click=self._handle_ok)