Inheritance

105 views
Skip to first unread message

sean...@gmail.com

unread,
Nov 24, 2016, 10:27:51 PM11/24/16
to Bokeh Discussion - Public
I noticed that when I try to inherit from an existing class that I need to add the following attributes:

__view_model__
__subtype__

Is there documentation on what view_model and subtype are and what are reasonable values? In my case, I'm inheriting from the Slider class but I would appreciate any guidance for handling any inheritance situations that involve these two attributes. Is there a list of possible values?

Best,
Sean

Bryan Van de Ven

unread,
Nov 27, 2016, 3:38:22 PM11/27/16
to bo...@continuum.io
Hi Sean,

I would not expect either of these to be needed under any normal circumstances, and in fact none of the examples in the repo or the user's guide make use of them. In brief, __view_model__ provides the BokehJS class name. Normally this is automatically inferred from the python class name, and so __view_model__ is only ever useful if they need to differ for some reason. And __subtype__ is even more esoteric, it provides a way for more than one python class to map to the same BokehJS class, but also round trip correctly to python. Currently this is only used for Plot and Figure and Chart (which all resolve to just "Plot" on the BokehJS side). None of these are intended are user-facing features.

As a concrete example, LatexLabel example inherits from Label, and does not need these:

http://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/latex.html#userguide-extensions-examples-latex

Can you provide code samples? Perhaps there is something you are trying that just needs to be expressed in a different way, or that we could document better. (It's also possible that I am wrong, and that there is some scenario where they are needed, that I just can't imagine offhand.)

Thanks,

Bryan
> --
> You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to bokeh+un...@continuum.io.
> To post to this group, send email to bo...@continuum.io.
> To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/b512d18f-1480-40e2-a0ae-8a5e5b6c7edc%40continuum.io.
> For more options, visit https://groups.google.com/a/continuum.io/d/optout.

sean...@gmail.com

unread,
Nov 27, 2016, 10:17:32 PM11/27/16
to Bokeh Discussion - Public
Bryan,

Here is a simple example where I am inheriting from the Slider class because I want a slider that requires a start and end value in order to instantiate:

#!/usr/bin/env python

import bokeh.models.widgets
import bokeh.plotting
import bokeh.io
import bokeh.layouts

class SLIDER(bokeh.models.widgets.Slider):

    #__subtype__ = ''
    #__view_model__ = 'Slider'

    def __init__(self, start, end):
        super(bokeh.models.widgets.Slider, self).__init__(start=start, end=end)

if __name__ == '__main__':
    p = bokeh.plotting.figure()
    p.circle(x=[0], y=[0])
    s = SLIDER(1, 10)
    bokeh.io.show(bokeh.layouts.layout([[p, s]]))

When the two lines are commented out, I get the following Bokeh error:

Bokeh Error

Module `SLIDER' does not exists. The problem may be two fold. Either a model was requested that's available in an extra bundle, e.g. a widget, or a custom model was requested, but it wasn't registered before first usage.


Sarah Bird

unread,
Nov 28, 2016, 12:08:26 AM11/28/16
to bo...@continuum.io
Hi Sean,

The error is referring to a lack of JS implementation. You need a model of the same name JS side. You can provide that by specifying __implementation__ python side. See docs: http://bokeh.pydata.org/en/latest/docs/user_guide/extensions.html

Bryan Van de Ven

unread,
Nov 28, 2016, 1:01:54 AM11/28/16
to bo...@continuum.io
Sean doesn't want a different JS implementation though, AFAICT, he just wants to customize the configuration of the existing one on the python side. This actually uncovers a blind spot in our (or at least in my) assumptions about subclassing Bokeh models. Namely that subclassing is equivalent to wanting to extend BokehJS, which is evidently not always true.

For completeness, if you want to subclass a Bokeh model, but keep the identical BokehJS model, the options are:

* provide a minimal __implementation__ that extends the BokehJS parent class trivially, so the new sublcass exists on the JS side too

* use the __subtype__ attribute to map the new python subclass to an already existing BokehJS class name

However, in truth, I'd actually suggest a completely different approach. Bokeh models are declarative, and completely determined by the values of their attributes. They typically have no other behaviours, with a couple of exceptions for "mega" models like Plot. if you are subclassing to add behaviour, I think you are asking for confusion. And if you are subclassing just to change the default configuration of a model (as you seem to be), then you don't actually need to subclass at all. I think a function would actually be much better:

def SLIDER(start, end, **kw):
return Slider(start=start, end=end, **kw)

Thanks,

Bryan
> To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/AD2313F7-0FAA-4395-A3B1-088BA4D89825%40continuum.io.
Message has been deleted

Marcus Donnelly

unread,
Nov 29, 2016, 5:53:52 AM11/29/16
to Bokeh Discussion - Public
Hi,

I have personally found subclassing useful for encapsulating combinations of plots and widgets. The example below adds a colourbar and slider to a Bokeh image plot in order to view different slices of a 3D dataset. (Before you say it I know the colourbar bit is redundant as Bokeh now includes one!) This approach allows me to have multiple ColourMap objects in a web app for viewing different datasets. I found this approach quite intuitive and useful, but from the previous discussion in this thread should I be looking at alternatives (e.g. in case this approach becomes unsupported?). Code as follows:

import numpy

from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource, Plot, LinearAxis
from bokeh.models.mappers import LinearColorMapper
from bokeh.models.ranges import Range1d
from bokeh.models.widgets import Slider
from bokeh.models.widgets.layouts import Column
from bokeh.core.properties import Instance
from bokeh.palettes import RdYlBu11
from bokeh.io import curdoc

# Define the class

class ColourMap(Column):

    __view_model__ = "Column"
    __subtype__ = "ColourMap"

    plot = Instance(Plot)
    cbar = Instance(Plot)

    power = Instance(Slider)

    datasrc = Instance(ColumnDataSource)
    cbarrange = Instance(ColumnDataSource)

    cmap = Instance(LinearColorMapper)

    def __init__(self,x,y,D):

        super(ColourMap,self).__init__()

        self.power = Slider(title = 'Power',name = 'Power',start = 1,end = numslices,step = 1,
                            value = round(numslices/2))
        self.power.on_change('value',self.inputchange)

        d = D[self.power.value]
        self.datasrc = ColumnDataSource(data={'x':x,'y':y,'d':[d],'D':D})

        self.cmap = LinearColorMapper(palette = RdYlBu11)

        r = Range1d(start = d.min(),end = d.max())       
        self.cbarrange = ColumnDataSource(data = {'range':[r]})

        self.plot = Figure(title="Colourmap plot",x_axis_label = 'x',y_axis_label = 'y',
                           x_range = [x[0],x[-1]],y_range=[y[0],y[-1]],
                           plot_height = 500,plot_width = 500)

        dx = x[1] - x[0]
        dy = y[1] - y[0]

        self.plot.image('d',source = self.datasrc,x = x[0]-dx/2, y = y[0]-dy/2,
                        dw = [x[-1]-x[0]+dx],dh = [y[-1]-y[0]+dy],
                        color_mapper = self.cmap)

        self.generate_colorbar()

        self.children.append(self.power)
        self.children.append(self.plot)
        self.children.append(self.cbar)

    def generate_colorbar(self,cbarlength = 500,cbarwidth = 50):

        pal = RdYlBu11

        minVal = self.datasrc.data['d'][0].min()
        maxVal = self.datasrc.data['d'][0].max()
        vals = numpy.linspace(minVal,maxVal,len(pal))

        self.cbar = Figure(tools = "",x_range = [minVal,maxVal],y_range = [0,1],
                           plot_width = cbarlength,plot_height = cbarwidth)

        self.cbar.toolbar_location = None
        self.cbar.min_border_left = 10
        self.cbar.min_border_right = 10
        self.cbar.min_border_top = 0
        self.cbar.min_border_bottom = 0
        self.cbar.xaxis.visible = False
        self.cbar.yaxis.visible = False
        self.cbar.extra_x_ranges = {'xrange':self.cbarrange.data['range'][0]}
        self.cbar.add_layout(LinearAxis(x_range_name = 'xrange'),'below')

        for r in self.cbar.renderers:
            if type(r).__name__ == 'Grid':
                r.grid_line_color = None

        self.cbar.rect(x = vals,y = 0.5,color = pal,width = vals[1]-vals[0],height = 1)

    def updatez(self):

        data = self.datasrc.data
        newdata = data
        d = data['d']
        d[0] = data['D'][self.power.value - 1]
        newdata['d'] = d
        self.datasrc.trigger('data',data,newdata)

    def updatecbar(self):

        minVal = self.datasrc.data['d'][0].min()
        maxVal = self.datasrc.data['d'][0].max()
        self.cbarrange.data['range'][0].start = minVal
        self.cbarrange.data['range'][0].end = maxVal

    def inputchange(self,attrname,old,new):

        self.updatez()
        self.updatecbar()

# Instantiate

numslices = 6
x = numpy.linspace(1,2,11)
y = numpy.linspace(2,4,21)
D = numpy.ndarray([numslices,y.size,x.size])
for i in range(numslices):
    for j in range(y.size):
        for k in range(x.size):
            D[i,j,k] = (y[j]*x[k])**(i+1) + y[j]*x[k]

curdoc().add_root(ColourMap(x,y,D))

sean...@gmail.com

unread,
Nov 29, 2016, 3:04:58 PM11/29/16
to Bokeh Discussion - Public
Bryan,

If I go down the subclass (2nd) route, I can't seem to add any new attributes to the class. This working example that builds upon what I showed above:

#!/usr/bin/env python

import bokeh.models.widgets
import bokeh.plotting
import bokeh.io
import bokeh.layouts

class SLIDER(bokeh.models.widgets.Slider):

    __subtype__ = ''
    __view_model__ = 'Slider'

    def __init__(self, start, end):
        super(bokeh.models.widgets.Slider, self).__init__(start=start, end=end)
        self.x = 5  # Can't do this!

p = bokeh.plotting.figure()
p.circle(x=[0], y=[0])
s = SLIDER(1, 10)
bokeh.io.curdoc().add_root(bokeh.layouts.layout([[p, s]]))


Which results in the following error:

AttributeError: unexpected attribute 'x' to SLIDER, possible attributes are callback, callback_policy, callback_throttle, disabled, end, height, name, orientation, sizing_mode, start, step, tags, title, value or width

It's not entirely clear how to best get around this. Choosing this route keeps everything neat and tidy within the class while returning an instance of a slider (your final suggestion), while simple, gets messy if there is more attributes/methods need to be added. Any suggestions would be greatly appreciated!

Best,
Sean

Bryan Van de Ven

unread,
Nov 29, 2016, 3:15:48 PM11/29/16
to bo...@continuum.io
Sean,

All public attributes of Bokeh models have to be properties. If you add new properties, you also definitely need a JS __implementation__ as well. However, you can add private regular attributes on the python side:

self._x = 5 # This will work

Thanks,

Bryan
> To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/32a2c452-d14f-4a93-a349-6c27bb333e2d%40continuum.io.

sean...@gmail.com

unread,
Nov 29, 2016, 3:46:21 PM11/29/16
to Bokeh Discussion - Public
Thanks, Bryan! That did the trick and, as always, thanks for your (and Sarah's) ongoing support!
Reply all
Reply to author
Forward
0 new messages