I have a class Collection
that holds a bunch of other class objects Thing
that all have the same attributes with different values. The Collection.plot(x, y)
method makes a scatter plot of the x
values vs. the y
values of all the collected Thing
objects like so:
from bokeh.plotting import figure, show
from bokeh.models import TapTool
class Thing:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def plot(self):
# Plot all data for thing
fig = figure()
fig.circle([1,2,3], [self.foo, self.bar, self.baz])
return fig
class Collection:
def __init__(self, things):
self.things = things
def plot(self, x, y):
# Configure plot
title = '{} v {}'.format(x, y)
fig = figure(title=title, tools=['pan', 'tap'])
taptool = fig.select(type=TapTool)
taptool.callback = RUN_THING_PLOT_ON_CLICK()
# Plot data
xdata = [getattr(th, x) for th in self.things]
ydata = [getattr(th, y) for th in self.things]
fig.circle(xdata, ydata)
return fig
Then I would make a scatter plot of all four Thing
sources' 'foo' vs. 'baz' values with:
A = Thing(2, 4, 6)
B = Thing(3, 6, 9)
C = Thing(7, 2, 5)
D = Thing(9, 2, 1)
X = Collection([A, B, C, D])
X.plot('foo', 'baz')
What I would like to have happen here is have each point on the scatter plot able to be clicked. On click, it would run the plot
method for the given Thing
, making a separate plot of all its 'foo', 'bar', and 'baz' values.
Any ideas on how this can be accomplished?
I know I can just load ALL the data for all the objects into a ColumnDataSource
and make the plot using this toy example, but in my real use case the Thing.plot
method does a lot of complicated calculations and may be plotting thousands of points. I really need it to actually run the Thing.plot
method and draw the new plot. Is that feasible?
Alternatively, could I pass the Collection.plot
method a list of all the Thing.plot
pre-drawn figures to then display on click?
Using Python>=3.6 and bokeh>=2.3.0. Thank you very much!