import numpy as np from bokeh.client import push_session from bokeh.plotting import figure from bokeh.document import Document from multiprocessing import Process, Pipe from bokeh.driving import count from NotebookTools import open_window class DataStreamProcess_1D(Process): def __init__(self,server, connecx, connecy, config, *args, **kwargs): self.connecx = connecx self.connecy = connecy self.config = config Process.__init__(self, *args, **kwargs) def run(self): plot_server_1d(connecx = self.connecx, connecy = self.connecy, config = self.config) def plot_server_1d( connecx = None, connecy = None, config = {'xlabel' : '','ylabel' : '', 'title': ''}): doc = Document() doc.title = config['title'] x = np.linspace(0,1,2) y = np.linspace(0,1,2) p = figure(title = config['title'],tools = 'pan,box_zoom,wheel_zoom,save,crosshair,resize,reset,hover') p.plot_height = 300 p.plot_width = 500 r1 = p.square(x, y, fill_color=None, line_color="green") r2 = p.line(x, y, line_color="green") p.xaxis.axis_label = config['xlabel'] p.yaxis.axis_label = config['ylabel'] # open a session to keep our local document in sync with server session = push_session(doc) def update(): # updating a single column of the the *same length* is OK if connecx.poll(0.1): print (p.plot_height) r1.data_source.data["x"] = connecx.recv() r2.data_source.data["x"] = r1.data_source.data["x"] r1.data_source.data["y"] = connecy.recv() r2.data_source.data["y"] = r1.data_source.data["y"] p.plot_height = int(np.round(r1.data_source.data["y"][0]*1000)) doc.add_periodic_callback(update, 50) doc.add_root(p) open_window('http://localhost:5006/?bokeh-session-id='+str(session.id)) session.loop_until_closed() # run forever class Plot1DServer: def __init__(self, config = {'xlabel' : '','ylabel' : '', 'title': ''}): self.connecx_ch, self.connecx_par = Pipe() self.connecy_ch, self.connecy_par = Pipe() self.data_stream = DataStreamProcess_1D(self, self.connecx_par, self.connecy_par, config) self.data_stream.start() def update(self, x,y): self.connecx_ch.send(x) self.connecy_ch.send(y) def close(): self.data_stream.close() class DataStreamProcess_2D(Process): def __init__(self,connecim,connecext, config, *args, **kwargs): self.connecim = connecim self.connecext = connecext self.config = config Process.__init__(self, *args, **kwargs) def run(self): plot_server_2d( connecim = self.connecim,connecext = self.connecext, config = self.config) def plot_server_2d(connecim = None,connecext = None, config = {'xlabel' : '','ylabel' : '', 'title': ''}): doc = Document() doc.title = config['title'] img = np.array([[0,0],[0,0]]) p = figure(title = config['title'], x_range=[0, img.shape[0]], y_range=[0, img.shape[1]], tools = 'pan,wheel_zoom,save,crosshair,resize,reset') from bokeh.models.tools import BoxZoomTool p.add_tools(BoxZoomTool(match_aspect = True)) p.plot_height = 400 p.plot_width = 400 from bokeh.palettes import Viridis256 r1 = p.image(image=[np.flipud(img)], x=[0], y=[0], dw=[img.shape[0]], dh=[img.shape[1]],palette = Viridis256) p.xaxis.axis_label = config['xlabel'] p.yaxis.axis_label = config['ylabel'] # open a session to keep our local document in sync with server session = push_session(doc) def update(): # updating a single column of the the *same length* is OK if connecim.poll(0.1): r1.data_source.data["image"] = [connecim.recv()] extent = connecext.recv() r1.data_source.data["x"] = [extent[0]] r1.data_source.data["dw"] = [extent[1] - extent[0]] r1.data_source.data["y"] = [extent[2]] r1.data_source.data["dh"] = [extent[3] - extent[2]] x = [extent[0],extent[1], extent[0], extent[1]] y = [extent[2],extent[2], extent[3], extent[3]] set_aspect(p, x, y, aspect=1) doc.add_periodic_callback(update, 50) doc.add_root(p) open_window('http://localhost:5006/?bokeh-session-id='+str(session.id), width = 500) session.loop_until_closed() # run forever class Plot2DServer: def __init__(self, config = {'xlabel' : '','ylabel' : '', 'title': ''}): self.connecext_ch, self.connecext_par = Pipe() self.connecim_ch, self.connecim_par = Pipe() self.data_stream = DataStreamProcess_2D(self.connecim_par,self.connecext_par, config) self.data_stream.start() def update(self,image ,extent): self.connecext_ch.send(extent) self.connecim_ch.send(image) def close(self): self.data_stream.join() from bokeh.models import Range1d def set_aspect(fig, x, y, aspect=1, margin=0.0): """Set the plot ranges to achieve a given aspect ratio. Args: fig (bokeh Figure): The figure object to modify. x (iterable): The x-coordinates of the displayed data. y (iterable): The y-coordinates of the displayed data. aspect (float, optional): The desired aspect ratio. Defaults to 1. Values larger than 1 mean the plot is squeezed horizontally. margin (float, optional): The margin to add for glyphs (as a fraction of the total plot range). Defaults to 0.1 """ xmin = min(xi for xi in x) xmax = max(xi for xi in x) ymin = min(yi for yi in y) ymax = max(yi for yi in y) width = (xmax - xmin)*(1+2*margin) if width <= 0: width = 1.0 height = (ymax - ymin)*(1+2*margin) if height <= 0: height = 1.0 xcenter = 0.5*(xmax + xmin) ycenter = 0.5*(ymax + ymin) r = aspect*(fig.plot_width/fig.plot_height) if width < r*height: width = r*height else: height = width/r fig.x_range.start = xcenter-0.5*width fig.x_range.end = xcenter+0.5*width fig.y_range.start = ycenter-0.5*height fig.y_range.end = ycenter+0.5*height