From 2815956d4291b8dddc9e64dc66dd0877bf33591d Mon Sep 17 00:00:00 2001 From: Sven Augustin Date: Wed, 26 May 2021 11:58:31 +0200 Subject: [PATCH] first try on dynamic adding/removing plots --- actor.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ director.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ kabuki.py | 42 ++++------------------------------- 3 files changed, 128 insertions(+), 38 deletions(-) create mode 100644 actor.py create mode 100644 director.py diff --git a/actor.py b/actor.py new file mode 100644 index 0000000..5339078 --- /dev/null +++ b/actor.py @@ -0,0 +1,63 @@ +from cacher import cacher +from plots import Plot0D, Plot1D, Plot2D +from sources import Camera, Source + + +def update_all(plt, cache): + plt.set(*cache.xy) + +def update_latest(plt, cache): + plt.set(cache.latest) + + +def decide_src_plt_cache(pvname): + pvname = pvname.upper() + + if pvname.endswith(":FPICTURE"): + print("Camera", pvname) + src = Camera(pvname) + plt = Plot2D + cache_size = 1 + update = update_latest + if not src.is_connected: + raise RuntimeError(f"{pvname} is not connected") + + else: + print("Source", pvname) + src = Source(pvname) + if not src.is_connected: + raise RuntimeError(f"{pvname} is not connected") + print(src.nelm) + if src.nelm == 0: + raise RuntimeError(f"{src}: {src.value}") + + if src.nelm == 1: + print("Scalar") + plt = Plot0D + cache_size = 100 + update = update_all + else: + print("Curve") + plt = Plot1D + cache_size = 1 + update = update_latest + + plt = plt() + plt.name = plt.fig.name = pvname #TODO this needs to be done in Plot* + + cache = cacher.add_source(src, size=cache_size) + + return src, plt, cache, update + + + +class Actor: + + def __init__(self, pvname, cache_size=100): + self.src, self.plt, self.cache, self._update = decide_src_plt_cache(pvname) + + def update(self): + self._update(self.plt, self.cache) + + + diff --git a/director.py b/director.py new file mode 100644 index 0000000..cc7774a --- /dev/null +++ b/director.py @@ -0,0 +1,61 @@ +from random import choice + +from bokeh.layouts import column, row +from bokeh.models import Button, Div, Spacer, TextInput +from bokeh.plotting import curdoc + +from actor import Actor + + +class Director: + + def __init__(self): + self.doc = doc = curdoc() + doc.title = choice("πŸ‘ΊπŸ‘Ή") + " kabuki" + + ti_add_pv = TextInput(value="", title="Add PV:") + ti_add_pv.on_change("value", self.on_add_pv) + + self.layout = layout = column(ti_add_pv) + doc.add_root(layout) + + + def add_pvs(self, *pvnames): + for n in pvnames: + self.add_pv(n) + + + def on_add_pv(self, attr, old, new): + print("CB Add PV:", attr, old, new) + self.add_pv(new) + + + def add_pv(self, pvname): + print("Add PV:", pvname) + + try: + a = Actor(pvname) + except Exception as e: + print("caught:", type(e), e) + return + + def add_header_widgets(fig): + btn = Button(label="πŸ—™", button_type="light", width=35) + lbl = Div(text=fig.name, align="center", style={"font-size": "150%"}) + res = column(row(btn, lbl), fig, Spacer(height=35)) + btn.on_click(lambda: self.remove_plot(res)) + return res + + fig = a.plt.fig + fig = add_header_widgets(fig) +# self.layout.children.append(fig) + self.layout.children.insert(1, fig) # 1 to skip TextInput + self.doc.add_periodic_callback(a.update, 1000) + + + def remove_plot(self, which): + print("Remove plot:", which) + self.layout.children.remove(which) + + + diff --git a/kabuki.py b/kabuki.py index f02fcb3..8eac258 100644 --- a/kabuki.py +++ b/kabuki.py @@ -1,44 +1,10 @@ -#!/usr/bin/env python - -from random import choice - -from bokeh.plotting import curdoc -from bokeh.layouts import column - from cacher import cacher -from plots import Plot0D, Plot1D, Plot2D -from sources import Camera, Source +from director import Director -src0 = Source("MTEST:RAND0") -src1 = Source("MTEST:ARR") -src2 = Camera("MTEST:CHAN-IMAGE:FPICTURE") - -cache0 = cacher.add_source(src0) -cache1 = cacher.add_source(src1, size=1) #TODO: statistics on the curves would need larger cache -cache2 = cacher.add_source(src2, size=1) - -p0 = Plot0D() -p1 = Plot1D() -p2 = Plot2D() - -def update(): - p0.set(*cache0.xy) - p1.set(cache1.latest) - p2.set(cache2.latest) - - -doc = curdoc() -doc.title = choice("πŸ‘ΊπŸ‘Ή") + " kabuki" -doc.add_periodic_callback(update, 1000) - -plt = column( - p0.fig, - p1.fig, - p2.fig -) - -doc.add_root(plt) +app = Director() +app.doc.add_periodic_callback(lambda: print(cacher), 10000) +app.add_pvs("MTEST:RAND0", "MTEST:ARR", "MTEST:CHAN-IMAGE:FPICTURE")