Esempio n. 1
0
class BokehBaseDisplay(with_metaclass(ABCMeta, BaseChartDisplay)):
    CellHandshake.addCallbackSniffer( lambda: "{'nostore_bokeh':!!window.Bokeh}")

    #get the bokeh version
    try:
        bokeh_version = pkg_resources.get_distribution("bokeh").parsed_version._version.release
    except:
        bokeh_version = None
        self.exception("Unable to get bokeh version")

    def __init__(self, options, entity, dataHandler=None):
        super(BokehBaseDisplay,self).__init__(options,entity,dataHandler)
        #no support for orientation
        self.no_orientation = True
    
    """
    Default implementation for creating a chart object.
    """
    def createBokehChart(self):
        return figure()

    def getPreferredOutputWidth(self):
        return super(BokehBaseDisplay,self).getPreferredOutputWidth() * 0.92

    @cache(fieldName="_loadJS")
    def getLoadJS(self):
        html, loadJS = _load_notebook_html(hide_banner=True)
        return loadJS

    def doRenderChart(self):        
        def genMarkup(chartFigure):
            return self.env.from_string("""
                    <script class="pd_save">
                    if ( !window.Bokeh && !window.autoload){{
                        window.autoload=true;
                        {loadJS}  
                    }}
                    </script>
                    {chartFigure}
                    {{%for message in messages%}}
                        <div>{{{{message}}}}</div>
                    {{%endfor%}}
                """.format(chartFigure=chartFigure, loadJS=self.getLoadJS())
            ).render(messages=self.messages)

        if BokehBaseDisplay.bokeh_version < (0,12):
            raise Exception("""
                <div>Incorrect version of Bokeh detected. Expected {0}, got {1}</div>
                <div>Please upgrade by using the following command: <b>!pip install --user --upgrade bokeh</b></div>
            """.format((0,12), BokehBaseDisplay.bokeh_version))
        clientHasBokeh = self.options.get("nostore_bokeh", "false") == "true"
        if not clientHasBokeh:          
            output_notebook(hide_banner=True)
        charts = self.createBokehChart()

        if not isinstance(charts, list):
            charts.add_tools(ResizeTool())
            #bokeh 0.12.5 has a non backward compatible change on the title field. It is now of type Title
            #following line is making sure that we are still working with 0.12.4 and below
            if hasattr(charts, "title") and hasattr(charts.title, "text"):
                charts.title.text = self.options.get("title", "")
            else:
                charts.title = self.options.get("title", "")
            charts.plot_width = int(self.getPreferredOutputWidth() - 10 )
            charts.plot_height = int(self.getPreferredOutputHeight() - 10  )
            charts.grid.grid_line_alpha=0.3
            return genMarkup(notebook_div(charts))
        else:
            from bokeh.layouts import gridplot
            ncols = 2
            nrows = len(charts)/2 + len(charts)%2
            
            w = self.getPreferredOutputWidth()/ncols if len(charts) > 1 else self.getPreferredOutputWidth()
            h = w * self.getHeightWidthRatio() if len(charts) > 1 else self.getPreferredOutputHeight()
            for chart in charts:
                chart.plot_width = int(w - 5)
                chart.plot_height = int (h - 5)

            return genMarkup(notebook_div(gridplot(charts, ncols=ncols, nrows=nrows)))
Esempio n. 2
0
class BokehBaseDisplay(with_metaclass(ABCMeta, BaseChartDisplay)):
    CellHandshake.addCallbackSniffer(
        lambda: "{'nostore_bokeh':!!window.Bokeh}")

    #get the bokeh version
    try:
        bokeh_version = pkg_resources.get_distribution(
            "bokeh").parsed_version._version.release
    except:
        bokeh_version = None
        self.exception("Unable to get bokeh version")

    def __init__(self, options, entity, dataHandler=None):
        super(BokehBaseDisplay, self).__init__(options, entity, dataHandler)
        #no support for orientation
        self.no_orientation = True

    """
    Default implementation for creating a chart object.
    """

    def createBokehChart(self):
        return figure()

    def getPreferredOutputWidth(self):
        return super(BokehBaseDisplay, self).getPreferredOutputWidth() * 0.92

    def get_common_figure_options(self):
        options = {}
        x_fields = self.getKeyFields()
        if len(x_fields) == 1 and (
                self.options.get("timeseries", 'false') == 'true'
                or self.dataHandler.isDateField(x_fields[0])):
            options["x_axis_type"] = "datetime"
        elif self.getBooleanOption("logx", False):
            options["x_axis_type"] = "log"

        if self.getBooleanOption("logy", False):
            options["y_axis_type"] = "log"
        return options

    @cache(fieldName="_loadJS")
    def getLoadJS(self):
        loadJS = self._load_notebook_html(hide_banner=True)
        return loadJS

    def colorPalette(self, size=None):
        # https://bokeh.pydata.org/en/latest/docs/reference/palettes.html
        # https://bokeh.pydata.org/en/latest/docs/reference/colors.html
        color = list(
            d3['Category10'][10])  # [ 'orangered', 'cornflowerblue',  ]
        # color = list(Spectral9).reverse()
        if size is None:
            return color
        elif size <= len(color):
            return color[0:size]
        elif size <= 256:
            return linear_palette(plasma(256), size)
        else:
            return linear_palette(
                plasma(256) + viridis(256) + magma(256), size)

    def doRenderChart(self):
        def genMarkup(chartFigure):
            s = chartFigure[0] if isinstance(chartFigure,
                                             tuple) else chartFigure
            d = chartFigure[1] if isinstance(chartFigure, tuple) else ''
            return self.env.from_string("""
                    <script class="pd_save">
                    function setChartScript() {{
                        if (!window.Bokeh) {{
                            setTimeout(setChartScript, 250)
                        }} else {{
                            var d = document.getElementById("pd-bkchartdiv-{p}")
                            if (d){{
                                var el = document.createElement('div')
                                el.innerHTML = `{chartScript}`
                                var chartscript = el.childNodes[1]
                                var s = document.createElement("script")
                                s.innerHTML = chartscript.innerHTML
                                d.parentNode.insertBefore(s, d)
                            }}
                        }}
                    }}
                    if (!window.Bokeh && !window.autoload){{
                        window.autoload=true;
                        {loadJS}
                    }}
                    setChartScript()
                    </script>
                    <div style="padding:5px" id="pd-bkchartdiv-{p}">{chartDiv}</div>
                    {{%for message in messages%}}
                        <div>{{{{message}}}}</div>
                    {{%endfor%}}
                """.format(chartScript=s.replace('</script>', '<\/script>'),
                           chartDiv=d,
                           loadJS=self.getLoadJS(),
                           p=self.getPrefix())).render(messages=self.messages)

        minBkVer = (0, 12, 9)
        if BokehBaseDisplay.bokeh_version < minBkVer:
            raise Exception("""
                <div>Incorrect version of Bokeh detected. Expected {0} or greater, got {1}</div>
                <div>Please upgrade by using the following command: <b>!pip install --user --upgrade bokeh</b></div>
            """.format(minBkVer, BokehBaseDisplay.bokeh_version))
        clientHasBokeh = self.options.get("nostore_bokeh", "false") == "true"
        if not clientHasBokeh:
            output_notebook(hide_banner=True)
        charts = self.createBokehChart()

        if not isinstance(charts, list):
            # charts.add_tools(ResizeTool())
            #bokeh 0.12.5 has a non backward compatible change on the title field. It is now of type Title
            #following line is making sure that we are still working with 0.12.4 and below
            if hasattr(charts, "title") and hasattr(charts.title, "text"):
                charts.title.text = self.options.get("title", "")
            else:
                charts.title = self.options.get("title", "")
            charts.plot_width = int(self.getPreferredOutputWidth() - 10)
            charts.plot_height = int(self.getPreferredOutputHeight() - 10)
            charts.grid.grid_line_alpha = 0.3
            return genMarkup(notebook_div(charts))
        else:
            from bokeh.layouts import gridplot
            ncols = 2
            nrows = len(charts) / 2 + len(charts) % 2

            w = self.getPreferredOutputWidth() / ncols if len(
                charts) > 1 else self.getPreferredOutputWidth()
            h = w * self.getHeightWidthRatio() if len(
                charts) > 1 else self.getPreferredOutputHeight()
            for chart in charts:
                chart.plot_width = int(w - 5)
                chart.plot_height = int(h - 5)

            return genMarkup(
                notebook_div(gridplot(charts, ncols=ncols, nrows=nrows)))

    # no longer part of bokeh
    #  https://github.com/bokeh/bokeh/pull/6928#issuecomment-329040653
    #  https://www.bvbcode.com/code/9xhgwcsf-2674609
    def _load_notebook_html(self,
                            resources=None,
                            hide_banner=False,
                            load_timeout=5000):
        from bokeh.core.templates import AUTOLOAD_NB_JS
        from bokeh.util.serialization import make_id
        from bokeh.resources import CDN

        if resources is None:
            resources = CDN

        element_id = make_id()

        js = AUTOLOAD_NB_JS.render(elementid='' if hide_banner else element_id,
                                   js_urls=resources.js_files,
                                   css_urls=resources.css_files,
                                   js_raw=resources.js_raw,
                                   css_raw=resources.css_raw_str,
                                   force=1,
                                   timeout=load_timeout)

        return js
class BokehStreamingDisplay(StreamingDisplay):
    CellHandshake.addCallbackSniffer(
        lambda: "{'nostore_bokeh':!!window.Bokeh}")

    def __init__(self, options, entity, dataHandler=None):
        super(BokehStreamingDisplay, self).__init__(options, entity,
                                                    dataHandler)
        self.handleId = None
        self.TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,tap,box_select,lasso_select"
        self.figure = figure(tools=self.TOOLS)
        self.figure.axis.major_label_text_font_size = "18pt"
        self.hover = HoverTool(tooltips=None, mode="vline")
        self.figure.add_tools(self.hover)

        self.comms_handle = None
        self.glyphRenderer = None
        self.setup()

    def setup(self):
        pass

    def createGlyphRenderer(self, figure):
        return None

    def updateGlyphRenderer(self, figure, glyphRenderer):
        pass

    def _concatArrays(self, a, b):
        if type(a) != type(b):
            raise Exception("Can't concatenate objects of different types")
        if isinstance(a, list):
            return a + b
        elif isinstance(a, np.ndarray):
            return np.concatenate((a, b))
        raise Exception("Can't concatenate: unsupported types")

    def _delWindowElements(self, array):
        if isinstance(array, list):
            del array[:len(array) - self.windowSize]
            return array
        elif isinstance(array, np.ndarray):
            array = np.delete(array, range(0, len(array) - self.windowSize))
            return array
        raise Exception("Can't delete: unsupported type")

    def _toNPArray(self, a):
        if isinstance(a, list):
            return np.array(a)
        elif isinstance(a, np.ndarray):
            return a
        raise Exception("Can't cast to np array: unsupported type")

    def doRender(self, handlerId):
        clientHasBokeh = self.options.get("nostore_bokeh", "false") == "true"
        if not clientHasBokeh:
            output_notebook(hide_banner=True)
        data = self.entity.getNextData()
        if data is None:
            return

        x = None
        y = None

        if isinstance(data, (list, np.ndarray)):
            x = list(
                range(self.windowSize)
            ) if self.glyphRenderer is None else self.glyphRenderer.data_source.data[
                'x']
            y = data if self.glyphRenderer is None else self._concatArrays(
                self.glyphRenderer.data_source.data['y'], data)
            if len(y) < self.windowSize:
                y = [0] * (self.windowSize - len(y)) + y
            elif len(y) > self.windowSize:
                y = self._delWindowElements(y)
        elif isinstance(data, pandas.core.frame.DataFrame):
            pd = pd.drop(pd.index[[0]])
            #pd.index = list(range(len(pd.index)))
            pd['x'] = list(range(len(pd.index)))
        else:
            x = data[0]
            y = data[1]

        if self.glyphRenderer is None:
            self.glyphRenderer = self.createGlyphRenderer(self.figure, x, y)
        else:
            self.updateGlyphRenderer(self.figure, self.glyphRenderer)

        if self.glyphRenderer is None:
            print("Error: no glyphRenderer found")
            return

        self.glyphRenderer.data_source.data['x'] = x
        self.glyphRenderer.data_source.data['y'] = y

        if not self.handleId:
            self.handleId = make_id()
            if self.figure not in _state.document.roots:
                _state.document.add_root(self.figure)
            target = notebook_div(self.figure, self.handleId)
            from IPython.display import display as ipythonDisplay, HTML, Javascript
            ipythonDisplay(HTML(target))
            self.comms_handle = _CommsHandle(get_comms(self.handleId),
                                             _state.document,
                                             _state.document.to_json())
        else:
            push_notebook(handle=self.comms_handle)
Esempio n. 4
0
class BokehBaseDisplay(with_metaclass(ABCMeta, BaseChartDisplay)):
    CellHandshake.addCallbackSniffer( lambda: "{'nostore_bokeh':!!window.Bokeh}")

    #get the bokeh version
    try:
        bokeh_version = pkg_resources.get_distribution("bokeh").parsed_version._version.release
    except:
        bokeh_version = None
        self.exception("Unable to get bokeh version")

    def __init__(self, options, entity, dataHandler=None):
        super(BokehBaseDisplay,self).__init__(options,entity,dataHandler)
        #no support for orientation
        self.no_orientation = True
    
    """
    Default implementation for creating a chart object.
    """
    def createBokehChart(self):
        return figure()

    def getPreferredOutputWidth(self):
        return super(BokehBaseDisplay,self).getPreferredOutputWidth() * 0.92

    def doRenderChart(self):
        def genMarkup(chartFigure):
            return self.env.from_string("""
                    {0}
                    {{%for message in messages%}}
                        <div>{{{{message}}}}</div>
                    {{%endfor%}}
                """.format(chartFigure)
            ).render(messages=self.messages)

        if BokehBaseDisplay.bokeh_version < (0,12):
            raise Exception("""
                <div>Incorrect version of Bokeh detected. Expected {0}, got {1}</div>
                <div>Please upgrade by using the following command: <b>!pip install --user --upgrade bokeh</b></div>
            """.format((0,12), BokehBaseDisplay.bokeh_version))
        clientHasBokeh = self.options.get("nostore_bokeh", "false") == "true"
        if not clientHasBokeh:          
            output_notebook(hide_banner=True)
        charts = self.createBokehChart()

        if not isinstance(charts, list):
            charts.add_tools(ResizeTool())
            charts.title = self.options.get("title", "")
            charts.plot_width = int(self.getPreferredOutputWidth() - 10 )
            charts.plot_height = int(self.getPreferredOutputHeight() - 10  )
            charts.grid.grid_line_alpha=0.3
            return genMarkup(notebook_div(charts))
        else:
            from bokeh.layouts import gridplot
            ncols = 1
            nrows = 2
            if(len(charts) > nrows):
                ncols = int(len(charts) / nrows)
                if(len(charts) % nrows != 0):
                    ncols = ncols + 1
            
            w = self.getPreferredOutputWidth()/ncols if len(charts) > 1 else self.getPreferredOutputWidth()
            h = self.getPreferredOutputWidth()/nrows if len(charts) > 1 else self.getPreferredOutputWidth()
            for chart in charts:
                chart.plot_width = int(w - 5)
                chart.plot_height = int (h - 5)

            return genMarkup(notebook_div(gridplot(charts, ncols=ncols, nrows=nrows)))