class DynamicPlotHandler(Handler): def __init__(self, network): self._network = weakref.ref(network) self.sources = {} self._last_time_list = None self._update_complete = False self._recurring_update = RecurringTask(self.plan_update_data, delay=5) self._pcb = None # Periodic callback self._ntcb = None # Next Tick callback super().__init__() @property def network(self): return self._network() def organize_data(self): self._log.debug("Organize Data") self.s = {} for point in self.network.trends: self.s[point.history.name] = (point.history, point.history.units) self.lst_of_trends = [his[0] for name, his in self.s.items()] def build_data_sources(self): sources = {} self.organize_data() for each in self.lst_of_trends: df = pd.DataFrame(each) df = df.reset_index() df["name"] = each.name df["units"] = str(each.units) df["time_s"] = df["index"].apply(str) df.states = each.states try: df = ( df.fillna(method="ffill") .fillna(method="bfill") .replace(["inactive", "active"], [0, 1]) ) except TypeError: df = df.fillna(method="ffill").fillna(method="bfill") sources[each.name] = ColumnDataSource( data=dict( x=df["index"], y=df[each.name], time=df["time_s"], name=df["name"], units=df["units"], ) ) return sources def build_plot(self): self._log.debug("Build Plot") self.stop_update_data() self.sources = self.build_data_sources() TOOLS = "pan,box_zoom,wheel_zoom,save,reset" self.p = Figure( x_axis_type="datetime", x_axis_label="Time", y_axis_label="Numeric Value", title="BAC0 Trends", tools=TOOLS, plot_width=800, plot_height=600, toolbar_location="above", ) self.p.background_fill_color = "#f4f3ef" self.p.border_fill_color = "#f4f3ef" self.p.extra_y_ranges = { "bool": Range1d(start=0, end=1.1), "enum": Range1d(start=0, end=10), } self.p.add_layout(LinearAxis(y_range_name="bool", axis_label="Binary"), "left") self.p.add_layout( LinearAxis(y_range_name="enum", axis_label="Enumerated"), "right" ) hover = HoverTool( tooltips=[ ("name", "@name"), ("value", "@y"), ("units", "@units"), ("time", "@time"), ] ) self.p.add_tools(hover) self.legends_list = [] length = len(self.s.keys()) if length <= 10: if length < 3: length = 3 color_mapper = dict(zip(self.s.keys(), d3["Category10"][length])) else: # This would be a very loaded trend... color_mapper = dict(zip(self.s.keys(), Spectral6[:length])) for each in self.lst_of_trends: if each.states == "binary": self.p.circle( "x", "y", source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=("{} | {} (OFF-ON)".format(each.name, each.description)), y_range_name="bool", size=10, ) # self.legends_list.append( # (("{} | {} (OFF-ON)".format(each.name, each.description)), [c]) # ) elif each.states == "multistates": self.p.diamond( "x", "y", source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=( "{} | {} ({})".format(each.name, each.description, each.units) ), y_range_name="enum", size=20, ) else: self.p.line( "x", "y", source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=( "{} | {} ({})".format(each.name, each.description, each.units) ), line_width=2, ) self.p.legend.location = "top_left" # legend = Legend(items=self.legends_list, location=(0, -60)) self.p.legend.click_policy = "hide" # self.p.add_layout(legend, "right") self.plots = [self.p] def update_data(self): self._log.debug("Update Data") doc = curdoc() # self.organize_data() if self._last_time_list: if self._last_time_list != self.s.keys(): self._list_have_changed = True self.stop_update_data() # doc.add_next_tick_callback(self.modify_document) self.modify_document(doc) else: self._list_have_changed = False l = [] for each in self.p.renderers: l.append(each.name) # for each in self.lst_of_trends: # df = pd.DataFrame(each) # df = df.reset_index() # df['name'] = each.name # df['units'] = str(each.units) # df['time_s'] = df['index'].apply(str) # try: # df = df.fillna(method='ffill').fillna( # method='bfill').replace(['inactive', 'active'], [0, 1]) # except TypeError: # df = df.fillna(method='ffill').fillna(method='bfill') index = l.index(each.name) # renderer = self.p.renderers[index] # new_data = {} # new_data['name'] = df['name'] # new_data['x'] = df['index'] # new_data['y'] = df[each.name] # if each.states == 'binary': # new_data['units'] = [each.units[int(x)] for x in df[each.name]] # elif each.states == 'multistates': # new_data['units'] = [ # each.units[int(math.fabs(x-1))] for x in df[each.name]] # else: # new_data['units'] = df['units'] # new_data['time'] = df['time_s'] # renderer.data_source.data = new_data try: new_data = self.build_data_sources() for each in self.lst_of_trends: self.sources[each.name].data = new_data[each.name].data except KeyError: self._log.warning( "Problem updating {} on chart, will try again next time.".format( each.name ) ) else: self._last_time_list = self.s.keys() # self.start_update_data() self._update_complete = True def modify_document(self, doc): doc.clear() self.build_plot() layout = gridplot(self.plots, ncols=2) doc.add_root(layout) self._pcb = doc.add_periodic_callback(self.update_data, 10000) return doc def plan_update_data(self): doc = curdoc() if self._update_complete == True: self._update_complete = False self._ntcb = doc.add_next_tick_callback(self.update_data) def stop_update_data(self): doc = curdoc() try: doc.remove_periodic_callback(self._pcb) except: pass if self._recurring_update.is_running: self._recurring_update.stop() while self._recurring_update.is_running: pass try: doc.remove_next_tick_callback(self._ntcb) except (ValueError, RuntimeError): pass # Already gone def start_update_data(self): if not self._recurring_update.is_running: try: self._recurring_update.start() while not self._recurring_update.is_running: pass except RuntimeError: pass
class DynamicPlotHandler(Handler): def __init__(self, network): self._network = weakref.ref(network) self.sources = {} self._last_time_list = None self._update_complete = False self._recurring_update = RecurringTask(self.plan_update_data, delay=5) super().__init__() @property def network(self): return self._network() def organize_data(self): self._log.debug('Organize Data') self.s = {} for point in self.network.trends: self.s[point.history.name] = (point.history, point.history.units) self.lst_of_trends = [his[0] for name, his in self.s.items()] def build_plot(self): self._log.debug('Build Plot') self.stop_update_data() self.organize_data() for each in self.lst_of_trends: df = pd.DataFrame(each) df = df.reset_index() # print(df) df['name'] = each.name df['units'] = str(each.units) df['time_s'] = df['index'].apply(str) df.states = each.states try: df = df.fillna(method='ffill').fillna(method='bfill').replace( ['inactive', 'active'], [0, 1]) except TypeError: df = df.fillna(method='ffill').fillna(method='bfill') self.sources[each.name] = ColumnDataSource( data=dict(x=df['index'], y=df[each.name], time=df['time_s'], name=df['name'], units=df['units'])) TOOLS = "pan,box_zoom,wheel_zoom,save,reset" self.p = Figure(x_axis_type="datetime", x_axis_label="Time", y_axis_label="Numeric Value", title='BAC0 Trends', tools=TOOLS, plot_width=1300, plot_height=600, toolbar_location='above') self.p.background_fill_color = "#f4f3ef" self.p.border_fill_color = "#f4f3ef" self.p.extra_y_ranges = { "bool": Range1d(start=0, end=1.1), "enum": Range1d(start=0, end=10) } self.p.add_layout(LinearAxis(y_range_name="bool", axis_label="Binary"), 'left') self.p.add_layout( LinearAxis(y_range_name="enum", axis_label="Enumerated"), 'right') hover = HoverTool(tooltips=[ ('name', '@name'), ('value', '@y'), ('units', '@units'), ('time', '@time'), ]) self.p.add_tools(hover) length = len(self.s.keys()) if length <= 10: if length < 3: length = 3 color_mapper = dict(zip(self.s.keys(), d3['Category10'][length])) else: # This would be a very loaded trend... color_mapper = dict(zip(self.s.keys(), Spectral6[:length])) for each in self.lst_of_trends: if each.states == 'binary': self.p.circle('x', 'y', source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=("%s | %s (OFF-ON)" % (each.name, each.description)), y_range_name="bool", size=10) elif each.states == 'multistates': self.p.diamond( 'x', 'y', source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=("%s | %s (%s)" % (each.name, each.description, each.units)), y_range_name="enum", size=20) else: self.p.line('x', 'y', source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=("%s | %s (%s)" % (each.name, each.description, each.units)), line_width=2) self.p.legend.location = 'bottom_left' self.p.legend.click_policy = "hide" self.plots = [ self.p, ] #@gen.coroutine def update_data(self): self._log.debug('Update Data') doc = curdoc() self.organize_data() if self._last_time_list: if self._last_time_list != self.s.keys(): self._list_have_changed = True self.stop_update_data() self.modify_document(doc) else: self._list_have_changed = False l = [] for each in self.p.renderers: l.append(each.name) for each in self.lst_of_trends: df = pd.DataFrame(each) df = df.reset_index() df['name'] = each.name df['units'] = str(each.units) df['time_s'] = df['index'].apply(str) try: df = df.fillna(method='ffill').fillna(method='bfill').replace( ['inactive', 'active'], [0, 1]) except TypeError: df = df.fillna(method='ffill').fillna(method='bfill') index = l.index(each.name) renderer = self.p.renderers[index] new_data = {} new_data['name'] = df['name'] new_data['x'] = df['index'] new_data['y'] = df[each.name] if each.states == 'binary': new_data['units'] = [each.units[int(x)] for x in df[each.name]] elif each.states == 'multistates': new_data['units'] = [ each.units[int(math.fabs(x - 1))] for x in df[each.name] ] else: new_data['units'] = df['units'] new_data['time'] = df['time_s'] renderer.data_source.data = new_data self._last_time_list = self.s.keys() # self.start_update_data() self._update_complete = True def modify_document(self, doc): curdoc().clear() try: curdoc().remove_periodic_callback(self.update_data) except: pass doc.clear() self.build_plot() # column(widgetbox(self.div_header_doc), layout = gridplot(self.plots, ncols=2) # widgetbox(self.div_header_notes), # widgetbox(self.div_footer) doc.add_root(layout) doc.add_periodic_callback(self.update_data, 10000) return doc def plan_update_data(self): doc = curdoc() if self._update_complete == True: self._update_complete = False doc.add_next_tick_callback(self.update_data) def stop_update_data(self): doc = curdoc() if self._recurring_update.is_running: self._recurring_update.stop() while self._recurring_update.is_running: pass try: doc.remove_next_tick_callback(self.update_data) except (ValueError, RuntimeError): pass # Already gone def start_update_data(self): if not self._recurring_update.is_running: try: self._recurring_update.start() while not self._recurring_update.is_running: pass except RuntimeError: pass
class DynamicPlotHandler(Handler): def __init__(self, network): self._network = weakref.ref(network) self.sources = {} self._last_time_list = None self._update_complete = False self._recurring_update = RecurringTask(self.plan_update_data, delay=5) self._pcb = None # Periodic callback self._ntcb = None # Next Tick callback super().__init__() @property def network(self): return self._network() def organize_data(self): self._log.debug("Organize Data") self.s = {} for point in self.network.trends: self.s[point.history.name] = (point.history, point.history.units) self.lst_of_trends = [his[0] for name, his in self.s.items()] def build_data_sources(self): sources = {} self.organize_data() for each in self.lst_of_trends: df = pd.DataFrame(each) df = df.reset_index() df["name"] = each.name df["units"] = str(each.units) df["time_s"] = df["index"].apply(str) df.states = each.states try: df = ( df.fillna(method="ffill") .fillna(method="bfill") .replace(["inactive", "active"], [0, 1]) ) except TypeError: df = df.fillna(method="ffill").fillna(method="bfill") sources[each.name] = ColumnDataSource( data=dict( x=df["index"], y=df[each.name], time=df["time_s"], name=df["name"], units=df["units"], ) ) return sources def build_plot(self): self._log.debug("Build Plot") self.stop_update_data() self.sources = self.build_data_sources() TOOLS = "pan,box_zoom,wheel_zoom,save,reset" self.p = Figure( x_axis_type="datetime", x_axis_label="Time", y_axis_label="Numeric Value", title="BAC0 Trends", tools=TOOLS, plot_width=800, plot_height=600, toolbar_location="above", ) self.p.background_fill_color = "#f4f3ef" self.p.border_fill_color = "#f4f3ef" self.p.extra_y_ranges = { "bool": Range1d(start=0, end=1.1), "enum": Range1d(start=0, end=10), } self.p.add_layout(LinearAxis(y_range_name="bool", axis_label="Binary"), "left") self.p.add_layout( LinearAxis(y_range_name="enum", axis_label="Enumerated"), "right" ) hover = HoverTool( tooltips=[ ("name", "@name"), ("value", "@y"), ("units", "@units"), ("time", "@time"), ] ) self.p.add_tools(hover) length = len(self.s.keys()) if length <= 10: if length < 3: length = 3 color_mapper = dict(zip(self.s.keys(), d3["Category10"][length])) else: # This would be a very loaded trend... color_mapper = dict(zip(self.s.keys(), Spectral6[:length])) for each in self.lst_of_trends: if each.states == "binary": self.p.circle( "x", "y", source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=("%s | %s (OFF-ON)" % (each.name, each.description)), y_range_name="bool", size=10, ) elif each.states == "multistates": self.p.diamond( "x", "y", source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=("%s | %s (%s)" % (each.name, each.description, each.units)), y_range_name="enum", size=20, ) else: self.p.line( "x", "y", source=self.sources[each.name], name=each.name, color=color_mapper[each.name], legend=("%s | %s (%s)" % (each.name, each.description, each.units)), line_width=2, ) self.p.legend.location = "bottom_right" self.p.legend.click_policy = "hide" self.plots = [self.p] def update_data(self): self._log.debug("Update Data") doc = curdoc() # self.organize_data() if self._last_time_list: if self._last_time_list != self.s.keys(): self._list_have_changed = True self.stop_update_data() # doc.add_next_tick_callback(self.modify_document) self.modify_document(doc) else: self._list_have_changed = False l = [] for each in self.p.renderers: l.append(each.name) # for each in self.lst_of_trends: # df = pd.DataFrame(each) # df = df.reset_index() # df['name'] = each.name # df['units'] = str(each.units) # df['time_s'] = df['index'].apply(str) # try: # df = df.fillna(method='ffill').fillna( # method='bfill').replace(['inactive', 'active'], [0, 1]) # except TypeError: # df = df.fillna(method='ffill').fillna(method='bfill') index = l.index(each.name) # renderer = self.p.renderers[index] # new_data = {} # new_data['name'] = df['name'] # new_data['x'] = df['index'] # new_data['y'] = df[each.name] # if each.states == 'binary': # new_data['units'] = [each.units[int(x)] for x in df[each.name]] # elif each.states == 'multistates': # new_data['units'] = [ # each.units[int(math.fabs(x-1))] for x in df[each.name]] # else: # new_data['units'] = df['units'] # new_data['time'] = df['time_s'] # renderer.data_source.data = new_data try: new_data = self.build_data_sources() for each in self.lst_of_trends: self.sources[each.name].data = new_data[each.name].data except KeyError: self._log.warning( "Problem updating {} on chart, will try again next time.".format( each.name ) ) else: self._last_time_list = self.s.keys() # self.start_update_data() self._update_complete = True def modify_document(self, doc): curdoc().clear() # doc = curdoc() try: curdoc().remove_periodic_callback(self._pcb) except: pass doc.clear() self.build_plot() layout = gridplot(self.plots, ncols=2) doc.add_root(layout) self._pcb = doc.add_periodic_callback(self.update_data, 10000) return doc def plan_update_data(self): doc = curdoc() if self._update_complete == True: self._update_complete = False self._ntcb = doc.add_next_tick_callback(self.update_data) def stop_update_data(self): doc = curdoc() if self._recurring_update.is_running: self._recurring_update.stop() while self._recurring_update.is_running: pass try: doc.remove_next_tick_callback(self._ntcb) except (ValueError, RuntimeError): pass # Already gone def start_update_data(self): if not self._recurring_update.is_running: try: self._recurring_update.start() while not self._recurring_update.is_running: pass except RuntimeError: pass
class BokehPlot(object): def __init__(self, device, points_list, *, title='My title', show_notes=True, update_data=True): self.device = device if len(points_list) < 3: raise ValueError("Provide at least 3 objects to the chart") self.points_list = points_list self.title = title self.units = {} self.show_notes = show_notes self.lst = self.points_list self.multi_states = self.device.multi_states self.binary_states = self.device.binary_states self.analog_units = self.device.analog_units plot = self.build_plot() self.device.properties.network.bokeh_document.add_plot( plot, infos=self.device.properties) #curdoc().add_root(plot) if update_data: self.device.properties.network.bokeh_document.add_periodic_callback( self.update_data, 100) print('Chart created, please reload your web page to see changes') # Get data def read_lst(self): df = self.device[self.lst] try: df = df.fillna(method='ffill').fillna(method='bfill').replace( ['inactive', 'active'], [0, 1]) except TypeError: df = df.fillna(method='ffill').fillna(method='bfill') df = df.reset_index() df['name'] = 'nameToReplace' df['units'] = 'waiting for refresh' df['time_s'] = df['index'].apply(str) return df def read_notes(self): notes_df = self.device.notes.reset_index() notes_df['value'] = -5 notes_df['desc'] = 'Notes' notes_df['time_s'] = notes_df['index'].apply(str) return notes_df def build_plot(self): df = self.read_lst() notes_df = self.read_notes() TOOLS = "pan,box_zoom,wheel_zoom,save,reset" self.p = Figure(x_axis_type="datetime", x_axis_label="Time", y_axis_label="Numeric Value", title=self.title, tools=TOOLS, plot_width=700, plot_height=600, toolbar_location='above') if self.show_notes: self.notes_source = ColumnDataSource( data=dict(x=notes_df['index'], y=notes_df['value'], time=notes_df['time_s'], desc=notes_df['desc'], units=notes_df[0])) self.p.asterisk( 'x', 'y', source=self.notes_source, name='Notes', #color = "#%06x" % random.randint(0x000000, 0x777777), legend='Notes', size=40) self.p.legend.location = 'top_left' self.p.extra_y_ranges = { "bool": Range1d(start=0, end=1.1), "enum": Range1d(start=0, end=10) } self.p.add_layout(LinearAxis(y_range_name="bool", axis_label="Binary"), 'left') self.p.add_layout( LinearAxis(y_range_name="enum", axis_label="Enumerated"), 'right') self.p.legend.location = "bottom_left" hover = HoverTool(tooltips=[ ('name', '@desc'), ('value', '@y'), ('units', '@units'), ('time', '@time'), ]) self.p.add_tools(hover) self.sources = {} if len(self.lst) <= 10: color_mapper = dict(zip(self.lst, d3['Category10'][len(self.lst)])) else: # This would be a very loaded trend... color_mapper = dict(zip(self.lst, Spectral6[:len(self.lst)])) for each in self.lst: try: df['name'] = df['name'].replace( 'nameToReplace', ('%s / %s' % (each, self.device[each]['description']))) except TypeError: continue self.sources[each] = ColumnDataSource(data=dict(x=df['index'], y=df[each], time=df['time_s'], name=df['name'], units=df['units'])) if each in self.binary_states: self.p.circle( 'x', 'y', source=self.sources[each], name=each, color=color_mapper[each], legend=("%s/%s (OFF-ON)" % (each, self.device[each]['description'])), y_range_name="bool", size=10) elif each in self.multi_states: self.p.diamond( 'x', 'y', source=self.sources[each], name=each, color=color_mapper[each], legend=("%s/%s (%s)" % (each, self.device[each]['description'], self.device[each].properties.units_state)), y_range_name="enum", size=20) else: self.p.line( 'x', 'y', source=self.sources[each], name=each, color=color_mapper[each], legend=("%s/%s (%s)" % (each, self.device[each]['description'], self.device[each].properties.units_state)), line_width=2) if self.show_notes: columns = [ TableColumn(field="x", title="Date", formatter=DateFormatter(format='yy-mm-dd')), TableColumn(field="units", title="Notes"), ] data_table = DataTable(source=self.notes_source, columns=columns) return (self.p, data_table) else: return (self.p, None) def update_data(self): if self.device.properties.network._started: df = self.read_lst() for renderer in self.p.renderers: name = renderer.name glyph_renderer = renderer new_data = {} if name in self.points_list: df['name'] = ('%s / %s' % (name, self.device[name]['description'])) new_data['x'] = df['index'] new_data['y'] = df[name] new_data['desc'] = df['name'] new_data['time'] = df['time_s'] if name in self.multi_states: new_data['units'] = [ self.multi_states[name][int(math.fabs(x - 1))] for x in df[name] ] elif name in self.binary_states: new_data['y'] = df[name] new_data['units'] = [ self.binary_states[name][int(x / 1)] for x in df[name] ] else: df['units'] = self.analog_units[name] new_data['units'] = df['units'] glyph_renderer.data_source.data = new_data elif name == 'Notes': notes_df = self.read_notes() new_data['x'] = notes_df['index'] new_data['y'] = notes_df['value'] new_data['desc'] = notes_df['desc'] new_data['units'] = notes_df[0] new_data['time'] = notes_df['time_s'] glyph_renderer.data_source.data = new_data
tground_syms = p1.triangle(tground['semi'] / a_rad, tground['msini'] * mjup, color='lightblue', fill_alpha=0.8, line_alpha=1., size=8) # transits - space tspace = Table.read( '/Users/tumlinson/Dropbox/LUVOIR_STDT/luvoir_simtools/planetspace/transit_space.dat', format='ascii', names=['name', 'msini', 'semi', 'mstar']) a_rad = 2.7 * tspace['mstar'] tground_syms = p1.diamond(tspace['semi'] / a_rad, tspace['msini'] * mjup, color='darkblue', fill_alpha=0.4, line_alpha=1., size=8) # RV rv = Table.read( '/Users/tumlinson/Dropbox/LUVOIR_STDT/luvoir_simtools/planetspace/rv.dat', format='ascii', names=['name', 'msini', 'semi', 'mstar']) a_rad = 2.7 * rv['mstar'] rv_syms = p1.circle(rv['semi'] / a_rad, rv['msini'] * mjup, color='red', fill_alpha=0.4, line_alpha=1., size=8)
class BokehPlot(object): def __init__(self, device, points_list, *, title='My title', show_notes=True, update_data=True): self.device = device self.points_list = points_list self.title = title self.units = {} self.show_notes = show_notes self.lst = self.points_list self.multi_states = self.device.multi_states self.binary_states = self.device.binary_states self.analog_units = self.device.analog_units plot = self.build_plot() self.device.properties.network.bokeh_document.add_plot(plot) if update_data: self.device.properties.network.bokeh_document.add_periodic_callback( self.update_data, 100) print('Chart created, please reload your web page to see changes') # Get data def read_lst(self): df = self.device[self.lst] try: df = df.fillna(method='ffill').fillna(method='bfill').replace( ['inactive', 'active'], [0, 1]) except TypeError: df = df.fillna(method='ffill').fillna(method='bfill') df = df.reset_index() df['name'] = 'nameToReplace' df['units'] = 'waiting for refresh' df['time_s'] = df['index'].apply(str) return df def read_notes(self): notes_df = self.device.notes.reset_index() notes_df['value'] = -5 notes_df['desc'] = 'Notes' notes_df['time_s'] = notes_df['index'].apply(str) return notes_df def build_plot(self): df = self.read_lst() notes_df = self.read_notes() TOOLS = "hover,resize,save,pan,box_zoom,wheel_zoom,reset" #plot_width=800, plot_height=600, self.p = Figure(x_axis_type="datetime", title=self.title, tools=TOOLS) if self.show_notes: self.notes_source = ColumnDataSource( data=dict(x=notes_df['index'], y=notes_df['value'], time=notes_df['time_s'], desc=notes_df['desc'], units=notes_df[0])) self.p.asterisk('x', 'y', source=self.notes_source, name='Notes', color="#%06x" % random.randint(0x000000, 0x777777), legend='Notes', size=40) self.p.legend.location = 'top_left' self.p.extra_y_ranges = { "bool": Range1d(start=0, end=1.1), "enum": Range1d(start=0, end=10) } self.p.add_layout(LinearAxis(y_range_name="bool"), 'left') self.p.add_layout(LinearAxis(y_range_name="enum"), 'right') hover = self.p.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ ('name', '@desc'), ('value', '@y'), ('units', '@units'), ('time', '@time'), ]) self.sources = {} for each in self.lst: try: df['name'] = df['name'].replace( 'nameToReplace', ('%s / %s' % (each, self.device[each]['description']))) except TypeError: continue self.sources[each] = ColumnDataSource(data=dict(x=df['index'], y=df[each], time=df['time_s'], name=df['name'], units=df['units'])) if each in self.binary_states: self.p.circle('x', 'y', source=self.sources[each], name=each, color="#%06x" % random.randint(0x000000, 0x777777), legend=each, y_range_name="bool", size=10) elif each in self.multi_states: self.p.diamond('x', 'y', source=self.sources[each], name=each, color="#%06x" % random.randint(0x000000, 0x777777), legend=each, y_range_name="enum", size=20) else: self.p.line('x', 'y', source=self.sources[each], name=each, color="#%06x" % random.randint(0x000000, 0x777777), legend=each, line_width=2) return self.p def update_data(self): if self.device.properties.network._started: df = self.read_lst() for renderer in self.p.renderers: name = renderer.name if name in self.points_list: glyph_renderer = renderer df['name'] = ('%s / %s' % (name, self.device[name]['description'])) glyph_renderer.data_source.data['x'] = df['index'] glyph_renderer.data_source.data['y'] = df[name] glyph_renderer.data_source.data['desc'] = df['name'] glyph_renderer.data_source.data['time'] = df['time_s'] if name in self.multi_states: glyph_renderer.data_source.data['units'] = [ self.multi_states[name][int(math.fabs(x - 1))] for x in df[name] ] elif name in self.binary_states: glyph_renderer.data_source.data['y'] = df[name] glyph_renderer.data_source.data['units'] = [ self.binary_states[name][int(x / 1)] for x in df[name] ] else: df['units'] = self.analog_units[name] glyph_renderer.data_source.data['units'] = df['units'] elif name == 'Notes': notes_df = self.read_notes() glyph_renderer = renderer glyph_renderer.data_source.data['x'] = notes_df['index'] glyph_renderer.data_source.data['y'] = notes_df['value'] glyph_renderer.data_source.data['desc'] = notes_df['desc'] glyph_renderer.data_source.data['units'] = notes_df[0] glyph_renderer.data_source.data['time'] = notes_df[ 'time_s']
class BokehPlot(object): def __init__(self, device, points_list, *, title = 'My title', show_notes = True, update_data = True): self.device = device self.points_list = points_list self.title = title self.units = {} self.show_notes = show_notes self.lst = self.points_list self.multi_states = self.device.multi_states self.binary_states = self.device.binary_states self.analog_units = self.device.analog_units plot = self.build_plot() self.device.properties.network.bokeh_document.add_plot(plot) if update_data: self.device.properties.network.bokeh_document.add_periodic_callback(self.update_data, 100) print('Chart created, please reload your web page to see changes') # Get data def read_lst(self): df = self.device[self.lst] try: df = df.fillna(method='ffill').fillna(method='bfill').replace(['inactive', 'active'], [0, 1]) except TypeError: df = df.fillna(method='ffill').fillna(method='bfill') df = df.reset_index() df['name'] = 'nameToReplace' df['units'] = 'waiting for refresh' df['time_s'] = df['index'].apply(str) return df def read_notes(self): notes_df = self.device.notes.reset_index() notes_df['value'] = -5 notes_df['desc'] = 'Notes' notes_df['time_s'] = notes_df['index'].apply(str) return notes_df def build_plot(self): df = self.read_lst() notes_df = self.read_notes() TOOLS = "hover,resize,save,pan,box_zoom,wheel_zoom,reset" #plot_width=800, plot_height=600, self.p = Figure(x_axis_type="datetime", title = self.title, tools = TOOLS) if self.show_notes: self.notes_source = ColumnDataSource( data=dict( x = notes_df['index'], y = notes_df['value'], time = notes_df['time_s'], desc = notes_df['desc'], units = notes_df[0] ) ) self.p.asterisk('x', 'y', source = self.notes_source, name = 'Notes', color = "#%06x" % random.randint(0x000000, 0x777777), legend='Notes', size = 40) self.p.legend.location = 'top_left' self.p.extra_y_ranges = {"bool": Range1d(start=0, end=1.1), "enum": Range1d(start=0, end=10)} self.p.add_layout(LinearAxis(y_range_name="bool"), 'left') self.p.add_layout(LinearAxis(y_range_name="enum"), 'right') hover = self.p.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ ('name', '@desc'), ('value', '@y'), ('units', '@units'), ('time', '@time'), ]) self.sources = {} for each in self.lst: try: df['name'] = df['name'].replace('nameToReplace', ('%s / %s' % (each, self.device[each]['description']))) except TypeError: continue self.sources[each] = ColumnDataSource( data=dict( x = df['index'], y = df[each], time = df['time_s'], name = df['name'], units = df['units'] ) ) if each in self.binary_states: self.p.circle('x', 'y', source = self.sources[each], name = each, color = "#%06x" % random.randint(0x000000, 0x777777), legend=each, y_range_name="bool", size = 10) elif each in self.multi_states: self.p.diamond('x', 'y', source = self.sources[each], name = each, color = "#%06x" % random.randint(0x000000, 0x777777), legend=each, y_range_name="enum", size = 20) else: self.p.line('x', 'y', source = self.sources[each], name = each, color = "#%06x" % random.randint(0x000000, 0x777777), legend=each, line_width = 2) return self.p def update_data(self): if self.device.properties.network._started: df = self.read_lst() for renderer in self.p.renderers: name = renderer.name if name in self.points_list: glyph_renderer = renderer df['name'] = ('%s / %s' % (name, self.device[name]['description'])) glyph_renderer.data_source.data['x'] = df['index'] glyph_renderer.data_source.data['y'] = df[name] glyph_renderer.data_source.data['desc'] = df['name'] glyph_renderer.data_source.data['time'] = df['time_s'] if name in self.multi_states: glyph_renderer.data_source.data['units'] = [self.multi_states[name][int(math.fabs(x-1))] for x in df[name]] elif name in self.binary_states: glyph_renderer.data_source.data['y'] = df[name] glyph_renderer.data_source.data['units'] = [self.binary_states[name][int(x/1)] for x in df[name]] else: df['units'] = self.analog_units[name] glyph_renderer.data_source.data['units'] = df['units'] elif name == 'Notes': notes_df = self.read_notes() glyph_renderer = renderer glyph_renderer.data_source.data['x'] = notes_df['index'] glyph_renderer.data_source.data['y'] = notes_df['value'] glyph_renderer.data_source.data['desc'] = notes_df['desc'] glyph_renderer.data_source.data['units'] = notes_df[0] glyph_renderer.data_source.data['time'] = notes_df['time_s']
# Set defaults fill_color=c_lcog, line_color=c_lcog, fill_alpha=a_lcog, line_alpha=a_lcog, # set visual properties for selected glyphs selection_color=c_lcog, selection_alpha=a_lcog, # set visual properties for non-selected glyphs nonselection_color=c_lcog, nonselection_alpha=a_lcog, legend = 'LCOGT-g') f_lcor = fig_lc.diamond('x', 'y', source=source_lco_r, size=9, # Set defaults fill_color=c_lcor, line_color=c_lcor, fill_alpha=a_lcor, line_alpha=a_lcor, # set visual properties for selected glyphs selection_color=c_lcor, selection_alpha=a_lcor, # set visual properties for non-selected glyphs nonselection_color=c_lcor, nonselection_alpha=a_lcor, legend = 'LCOGT-r') # Modify the legend's appearance fig_lc.legend.location = "bottom_right" fig_lc.legend.border_line_width = 3 fig_lc.legend.border_line_color = "navy" fig_lc.legend.border_line_alpha = 0.5 fig_lc.legend.label_text_font_style = "bold" fig_lc.legend.label_text_font_size = '9pt' fig_lc.legend.spacing = -3 fig_lc.legend.label_standoff = 0