""" """ import os import numpy as np import datetime try: from nicegui import app, ui, events, Client except ModuleNotFoundError as e: print('Module `nicegui` not found.') exit(1) try: import plotly.graph_objects as pgo 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) 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 parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawTextHelpFormatter, prefix_chars='-', add_help=False, ) parser.add_argument( '--host', type=str, default='', help='default 127.0.0.1') parser.add_argument( '--port', type=str, default='8085', help='default 8080') parser.add_argument( '-v', '--verbose', action='store_true', help='Verbose output') parser.add_argument( '-D', '--debug', action='store_true', help=argparse.SUPPRESS) parser.add_argument( '-h', '--help', action='store_true', help='Show this help message and exit') args = parser.parse_args() if args.debug: DEBUG = True print(f'[DEBUG] args: {args}') print(f'[DEBUG] __file__: {__file__}') print(f'[DEBUG] cwd: {os.getcwd()}') print(f'[DEBUG] host: {args.host}') print(f'[DEBUG] port: {args.port}') if args.help: parser.print_help() exit(0) return args def np2pgo(x, y, label=None): res = {'x': x, 'y': y} if label is not None: res.update({'name': label}) return res async def pick_file() -> None: result = await local_file_picker.local_file_picker('./data', multiple=False) # ui.notify(f'You chose {result}') 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))) async def get_data(filename=ELM['filename']): data = np.genfromtxt(fname=filename, skip_header=1) if len(data) > 0 and len(data[0]) > 6: names = ['time', 'ts', 'ta', 'tb', 'tc', 'td', 'hp', 'hl'] else: names = ['time', 'ts', 'ta', 'tb', 'hp', 'hl'] data = np.genfromtxt( fname=filename, dtype=None, skip_header=1, converters={ 0: lambda x: datetime.datetime.strptime(x, '%Y%m%d%H%M%S')}, names=names, encoding='utf-8' ) return data 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))) ELM['fig'].data = [] ELM['plot'].visible = False ELM['plot'].update() 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: 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'))) ELM['fig'].add_trace(pgo.Scatter(**np2pgo(data['time'], data['hp'], 'Heat')), row=2, col=1) ELM['plot'].visible = True ELM['plot'].update() @ui.page('/') async def index(): await ui.context.client.connected() # initialize objects ELM['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.colors(primary='#4888c4') with ui.row(align_items="center").classes('w-full'): ui.button('Choose file', on_click=pick_file, icon='folder') 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' # ELM['fig'] = pgo.Figure() ELM['fig'].update_layout( height=600, #template='plotly_white' template=ELM['plotly_dark'] if dark_mode.value else ELM['plotly_light'], ) ELM['plot'] = ui.plotly(ELM['fig']).classes('w-full') # 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=ELM['name'], dark=False, native=False, show=False, storage_secret='tempview') def main(args): return 0 if __name__ in {'__main__', '__mp_main__'}: args = main_cli() main_gui(args)