import justpy as jp import asyncio import matplotlib.pyplot as plt button_classes = 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full' label_div_classes = 'py-2 px-4' checkbox_classed = label_div_classes labelnames_classes = 'font-bold' panel_div_classes = 'space-x-4 border' controls_div_classes = 'flex space-x-4 border' controls_group_classes = 'flex space-x-4' input_classes = "m-2 bg-gray-200 border-2 border-gray-200 rounded w-20 text-gray-700 focus:outline-none focus:bg-white focus:border-purple-500" class QOLPushButton(jp.Button): def __init__(self, **kwargs): super().__init__(**kwargs) self.set_classes('bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full') class QOLPushButtonNoUndo(QOLPushButton): def __init__(self, **kwargs): super().__init__(**kwargs) self.set_classes('bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-full') class QOLParamHeader(jp.Div): def __init__(self, label='NameNotSet', value=None, **kwargs): super().__init__(**kwargs) root = self root.label=label root.set_classes(label_div_classes) root.data[label]=None jp.Span(text=f'{label}: ', classes=labelnames_classes, a=root) self.setValue(value) def getValue(self): return self.data[self.label] def setValue(self, val): # print(f'setting {self.label} to {val}') self.data[self.label]=val class QOLParamReadOnly(QOLParamHeader): """ Read Only from the web point of view """ def __init__(self, label='NameNotSet', **kwargs): super().__init__(label=label, **kwargs) root = self jp.Span(model=[root, label], a=root) class QOLParamReadWrite(QOLParamHeader): def __init__(self, label='NameNotSet', **kwargs): super().__init__(label=label, **kwargs) root = self self.input=jp.InputChangeOnly(classes=input_classes, model=[root, label], a=root, spellcheck="false") class QOLCheckbox(jp.Label): def __init__(self, label='NameNotSet', **kwargs): super().__init__(**kwargs) root = self root.data['checked'] = False root.label=label root.set_classes(checkbox_classed) checkbox = jp.Input(type='checkbox', model=[root, 'checked'], classes='form-checkbox', a=root) caption = jp.Span(text=label, a=root) def getValue(self): return self.data['checked'] def setValue(self, val): self.data['checked'] = val class QOLTimeLog(jp.Div): def __init__(self, **kwargs): super().__init__(**kwargs) root = self root.traces = None; # log must have 'plot' and 'clear_data' methods root.getNextDataFile = None root.set_classes(panel_div_classes) dcontrols = jp.Div(a=root) dcontrols.set_classes(controls_div_classes) bclear = QOLPushButtonNoUndo(a=dcontrols, text='Clear log', click=self._clear_data) breplot = QOLPushButton(a=dcontrols, text='Replot', click=self._replot) self.save_controls = QOLSaveControls(a=dcontrols) self.save_controls.save.on('click', self._save) self.save_controls.next_file.on('click', self._next_file) self.update_interval = QOLParamReadOnly(label='UpdateInterval',value=5,a=dcontrols) self.autosave_interval = QOLParamReadOnly(label='AutoSaveInterval',value=10,a=dcontrols) self.chart = jp.Matplotlib(a=root) self.start_task() def start_task(self): jp.run_task( self.update_loop() ) def stop_tasks(self): pass async def _next_file(self, msg): print('asked for _next_file') self.next_file() await self.update() def next_file(self): if self.getNextDataFile is not None: fname = self.getNextDataFile() self.save_controls.file_name.setValue( fname ) self.traces.clear_last_saved_pos() async def _save(self, msg): self.save() def save(self): print('asked to save') fname = self.save_controls.file_name.getValue() print(fname) if fname is None: return if self.traces is None: return print(f'saving to {fname}') self.traces.save(fname) async def _clear_data(self, msg): self.clear_data() await self.update() def clear_data(self): traces = self.traces if traces is not None: traces.clear_data() self.plot() async def _replot(self, msg): self.plot() await self.update() async def update_loop(self, update_interval=None): if update_interval is not None: self.update_interval.setValue(update_interval) while True: self.plot() try: strwtime = self.update_interval.getValue() wtime = float(strwtime) except ValueError: wtime =2 print(f'Warning cannot convert {strwtime} setting to {wtime}') await self.update() await asyncio.sleep(wtime) def plot(self): traces = self.traces f = plt.figure() if traces is None: plt.title('Log data is unavailable') else: traces.plot() self.chart.set_figure(f) plt.close(f) class QOLSaveControls(jp.Div): def __init__(self, **kwargs): super().__init__(**kwargs) root = self root.set_classes(controls_group_classes) root.save=QOLPushButton(a=root, text='Save', name='Save', click=self._save) root.next_file=QOLPushButton(a=root, text='Next file', name='NextFile', click=self._next_file) self.autosave=QOLCheckbox(label='autosave', a=root ) self.file_name=QOLParamReadOnly(label='FileName', a=root) def _save(self, msg): """ This is place holder: user should assign 'save' with a callback """ print('Warning: `save` callback is not set') pass def _next_file(self, msg): """ This is place holder: user should assign 'next_file' with a callback """ print('Warning: `next_file` callback is not set') pass def gui_test(): return wp if __name__ == '__main__': wp = jp.WebPage(delete_flag=False) rw = QOLParamReadWrite(label='ReadWriteParam', a=wp) rw.setValue(12345) log = QOLTimeLog(a=wp) def test(self, msg): print(rw.getValue()) QOLPushButtonNoUndo(text='Danger', a=wp, onclick=test) # sc = QOLSaveControls(a=wp) jp.justpy(gui_test)