class BokehDocument(InstancesMixin): def __init__(self, title='Live Trending'): self.document = Document(title=title) self.plots = [] self.widgets = [ None, ] self._log = logging.getLogger("bokeh").setLevel(logging.INFO) def add_plot(self, new_plot_and_widget, linked_x_axis=True, infos=None): self.document.clear() new_plot, widget = new_plot_and_widget new_plot.x_range.bounds = None new_plot.y_range.bounds = None for key, plot in enumerate(self.plots): if new_plot.title == plot.title: self.plots.pop(key) self.plots.append(new_plot) break else: self.plots.append(new_plot) if self.widgets: pass else: # For now, let's deal with only one widget self.widgets.append(widget) self.widgets[0] = widget if linked_x_axis: for plot in self.plots[1:]: plot.x_range = self.plots[0].x_range plot.x_range.bounds = None plot.y_range.bounds = None div_header_doc = Div(text="""<div class="header"> <H1> BAC0 Trending Tools </H1> <h2>For %s (Address : %s | Device ID : %s)</h2></div>""" % (infos.name, infos.address, infos.device_id)) div_header_notes = Div( text= """<div class="TableTitle"><H1> Notes from controller</H1></div>""" ) div_footer = Div( text= """<div class="footer"><p> <a href="http://www.servisys.com">Servisys inc.</a> | <a href="https://pythoninthebuilding.wordpress.com/">Python in the building</a></p></div>""" ) layout = column(widgetbox(div_header_doc), gridplot(self.plots, ncols=2), widgetbox(div_header_notes), row(self.widgets), widgetbox(div_footer)) self.document.add_root(layout) #curdoc().add_root(layout) def add_periodic_callback(self, cb, update=100): self.document.add_periodic_callback(cb, update)
class BokehDocument(InstancesMixin): def __init__(self, title='Live Trending'): self.document = Document(title=title) self.plots = [] logging.getLogger("requests").setLevel(logging.INFO) logging.getLogger("bokeh").setLevel(logging.INFO) def add_plot(self, new_plot, linked_x_axis=True): self.document.clear() new_plot.x_range.bounds = None new_plot.y_range.bounds = None for key, plot in enumerate(self.plots): if new_plot.title == plot.title: self.plots.pop(key) self.plots.append(new_plot) break else: self.plots.append(new_plot) if linked_x_axis: for plot in self.plots[1:]: plot.x_range = self.plots[0].x_range plot.x_range.bounds = None plot.y_range.bounds = None if len(self.plots) > 1: number_of_rows = int(round(len(self.plots) / 2)) rows = [] number_of_columns = 2 i = 0 for each in range(number_of_rows): rows.append( list(plot for plot in self.plots[i:i + number_of_columns])) i += number_of_columns layout = gridplot(rows) else: print('Layout...') layout = VBox(children=self.plots) self.document.add_root(layout) def add_periodic_callback(self, cb, update=100): self.document.add_periodic_callback(cb, update)
class BokehDocument(InstancesMixin): def __init__(self, title = 'Live Trending'): self.document = Document(title = title) self.plots = [] logging.getLogger("requests").setLevel(logging.INFO) logging.getLogger("bokeh").setLevel(logging.INFO) def add_plot(self, new_plot, linked_x_axis = True): self.document.clear() new_plot.x_range.bounds = None new_plot.y_range.bounds = None for key, plot in enumerate(self.plots): if new_plot.title == plot.title: self.plots.pop(key) self.plots.append(new_plot) break else: self.plots.append(new_plot) if linked_x_axis: for plot in self.plots[1:]: plot.x_range = self.plots[0].x_range plot.x_range.bounds = None plot.y_range.bounds = None if len(self.plots) > 1: number_of_rows = int(round(len(self.plots) / 2)) rows = [] number_of_columns = 2 i = 0 for each in range(number_of_rows): rows.append(list(plot for plot in self.plots[i:i+number_of_columns])) i += number_of_columns layout = gridplot(rows) else: print('Layout...') layout = VBox(children=self.plots) self.document.add_root(layout) def add_periodic_callback(self, cb, update = 100): self.document.add_periodic_callback(cb,update)
class PlottingExtension(Callback): """Base class for extensions doing Bokeh plotting. Parameters ---------- document_name : str The name of the Bokeh document. Use a different name for each experiment if you are storing your plots. ##start_server : Removed, as it has a number of flaws inkl. zombi bokeh-server processes! server_url : str, optional Url of the bokeh-server. Ex: when starting the bokeh-server with ``bokeh-server --ip 0.0.0.0`` at ``alice``, server_url should be ``http://alice:5006``. Defaults to http://localhost:5006. clear_document : bool, optional Whether or not to clear the contents of the server-side document upon creation. If `False`, previously existing plots within the document will be kept. Defaults to `True`. """ def __init__(self, document_name, server_url=None, clear_document=True): super(PlottingExtension, self).__init__() self.document_name = document_name self.server_url = DEFAULT_SRV_URL if server_url is None else server_url self.session = Session(root_url=self.server_url) self.document = Document() self._setup_document(clear_document) def _setup_document(self, clear_document=False): self.session.use_doc(self.document_name) self.session.load_document(self.document) if clear_document: self.document.clear() self._document_setup_done = True def __getstate__(self): state = self.__dict__.copy() state.pop('_sub', None) state.pop('session', None) state.pop('_push_thread', None) return state def __setstate__(self, state): self.__dict__.update(state) self.session = Session(root_url=self.server_url) self._document_setup_done = False def on_callback(self, logs={}): if not self._document_setup_done: self._setup_document() @property def push_thread(self): if not hasattr(self, '_push_thread'): self._push_thread = PushThread(self.session, self.document) self._push_thread.start() return self._push_thread def store_data(self, obj): self.push_thread.put(obj, PushThread.PUT) def push_document(self, after_training=False): self.push_thread.put(after_training, PushThread.PUSH)
class PlottingExtension(SimpleExtension): """Base class for extensions doing Bokeh plotting. Parameters ---------- document_name : str The name of the Bokeh document. Use a different name for each experiment if you are storing your plots. start_server : bool, optional Whether to try and start the Bokeh plotting server. Defaults to ``False``. The server started is not persistent i.e. after shutting it down you will lose your plots. If you want to store your plots, start the server manually using the ``bokeh-server`` command. Also see the warning above. server_url : str, optional Url of the bokeh-server. Ex: when starting the bokeh-server with ``bokeh-server --ip 0.0.0.0`` at ``alice``, server_url should be ``http://alice:5006``. When not specified the default configured by ``bokeh_server`` in ``.blocksrc`` will be used. Defaults to ``http://localhost:5006/``. clear_document : bool, optional Whether or not to clear the contents of the server-side document upon creation. If `False`, previously existing plots within the document will be kept. Defaults to `True`. """ def __init__(self, document_name, server_url=None, start_server=False, clear_document=True, **kwargs): self.document_name = document_name self.server_url = (config.bokeh_server if server_url is None else server_url) self.start_server = start_server self.sub = self._start_server_process() if self.start_server else None self.session = Session(root_url=self.server_url) self.document = Document() self._setup_document(clear_document) super(PlottingExtension, self).__init__(**kwargs) def _start_server_process(self): def preexec_fn(): """Prevents the server from dying on training interrupt.""" signal.signal(signal.SIGINT, signal.SIG_IGN) # Only memory works with subprocess, need to wait for it to start logger.info('Starting plotting server on localhost:5006') self.sub = Popen('bokeh-server --ip 0.0.0.0 ' '--backend memory'.split(), stdout=PIPE, stderr=PIPE, preexec_fn=preexec_fn) time.sleep(2) logger.info('Plotting server PID: {}'.format(self.sub.pid)) def _setup_document(self, clear_document=False): self.session.use_doc(self.document_name) self.session.load_document(self.document) if clear_document: self.document.clear() self._document_setup_done = True def __getstate__(self): state = self.__dict__.copy() state['sub'] = None state.pop('session', None) state.pop('_push_thread', None) return state def __setstate__(self, state): self.__dict__.update(state) if self.start_server: self._start_server_process() self.session = Session(root_url=self.server_url) self._document_setup_done = False def do(self, which_callback, *args): if not self._document_setup_done: self._setup_document() @property def push_thread(self): if not hasattr(self, '_push_thread'): self._push_thread = PushThread(self.session, self.document) self._push_thread.start() return self._push_thread def store(self, obj): self.push_thread.put(obj, PushThread.PUT) def push(self, which_callback): self.push_thread.put(which_callback, PushThread.PUSH)