I would like to refresh a bokeh document so I can replace old plots with new ones.
However, right now I just get the new plots appended to the document so the old ones don't go away.
#myfile.py
from bokeh.plotting import curdoc, figure
doc = curdoc()
p1 = figure(width=1500, height=230, active_scroll="wheel_zoom")
doc.add_root(p1)
doc.clear()
p2 = figure(width=1500, height=500, active_scroll="wheel_zoom")
doc.add_root(p2)
This results in the second plot being displayed after the first plot, though the expected behavior I am looking for is the second plot replacing the first plot. How can I resolve this? I am running this on a bokeh server via bokeh serve --show myfile.py
The best way to accomplish something like this is to have a top level layout of some kind (e.g. row or column) that has the content you want to replace inside it. Then when you want to replace things, keep the layout container, but change the value of its children property:
from bokeh.plotting import curdoc, figure
from bokeh.layouts import row
doc = curdoc()
p1 = figure(width=1500, height=230, active_scroll="wheel_zoom")
layout = row(p1)
doc.add_root(layout)
p2 = figure(width=1500, height=500, active_scroll="wheel_zoom")
layout.children[0] = p2
You can see a similar technique in the Crossfilter example.
Just in case anyone is struggling on how to set the children for layouts when there are multiple elements (say, widgets, more figures, rows etc), you can do so by wrapping the elements in a layout and assigning the children property directly:
p2 = figure(width=1500, height=500, active_scroll="wheel_zoom")
p3 = figure(width=1500, height=500, active_scroll="wheel_zoom")
new_layout = row(p2, p3)
layout.children = new_layout.children
Related
The following script produces a nice scatterplot as in the picture below
from holoviews import extension, dim, opts, Scatter
from pandas import read_csv
extension('bokeh')
url = 'https://raw.githubusercontent.com/mwaskom/seaborn-data/master/penguins.csv'
df = read_csv(url)
scatterplot = Scatter(df, 'flipper_length_mm', ['body_mass_g','island'] )
scatterplot.opts( color=dim('island').str(), cmap=['red','green','blue'] )
Now, suppose I want to use markers in place of colors for island.
Is there something equivalent to cmap (as in the last line of the script above) for marker?
You can update the code as below to add markers. I have left the colors as is. If you don't need it, can remove that and just keep the markers alone... You can use markers from here
scatterplot.opts( color=dim('island').str(), cmap=['red','green','blue'],
marker=dim('island').categorize({'Torgersen':'circle', 'Biscoe':'diamond', 'Dream':'dash'}) )
First, I created a scatter plot out of geogr. coordinates. If i click on one of these circles a second line-plot next to that scatter plot shows further informations depending on what circle i've clicked. That means i have to update the current ColumnDataSource shown in the line-plot by a new one. But if i click on one of those circles the current Source will not be updated. The line-plot still shows the dataset of the old Source.
I'll try to give you a short example of what i've done so far:
def callback(attr, old, new):
# Depending on what circle i've clicked i start a SQL request
# to gain my dataset i want to plot and the new title of the diagram.
# To change the title actually works:
line_plot.title.text = 'new_title'
# "source_new_values" is a ColumnDataSource created out of a
# SQL-request of my database.
# To change the current source doesn't work. The line-plot is still
# showing the old dataset.
source_current_values = source_new_values
scatter_plot = figure(x_axis_label='lat', y_axis_label='lon')
scatter_plot.circle(x='long', y='lat', source=source_coordinates)
# I use the indices to identify what circle was clicked.
source_coordinates.selected.on_change('indices', callback)
line_plot = figure(x_axis_label='time', x_axis_type='datetime',
y_axis_label='values', title='title')
line_plot.line(x='date', y='value', source=source_current_values)
The solution for tat Problem is I'm not able to update the source by a ColumnDataSource, but by a Dictionary using:
source_current_values.data = Dict("some content")
I'd like to draw a vertical line on my Bokeh plot which gets moved around by javascript in the browser at runtime. (It's a timebar that marks the current time on a time series plot.)
For drawing a static vertical line, I'm using:
from bokeh.models import Span
timebar = Span(location=where_I_want_the_timebar, dimension=height)
my_figure.add_layout(timebar)
In order to enable the interactivity, I think I need to get the location from a ColumnDataSource. However, I can't figure out how to do that, because Span does not accept a source argument.
Alternatively, is there another way for me to move the timebar at runtime?
I found a workaround. In python:
from bokeh.models import Span
timebar = Span(name='timebar' location=where_I_want_the_timebar, dimension=height)
my_figure.add_layout(timebar)
Then in javascript in the browser:
let timebar = Bokeh.documents[0].get_model_by_name('timebar')
timebar.attributes.location = my_new_desired_location
timebar.change.emit()
If someone posts a way to use a ColumnDataSource I will accept that answer.
I am displaying 2d data as images of varying shapes in a bokeh server, and therefore need to dynamically update not only the image's data source, but also its dw, dh, x, and y properties. In the dummy example below, these changes are made in a callback function which is connected to a Button widget.
I've figured out that I need to access the glyph attribute of the image's GlyphRenderer object, and I can do so through its update() method (see code). But the changes don't take effect until I click the toolbar's Reset button. I've noticed that the changes also mysteriously take effect the second time I activate the callback() function. What is the proper way to make these changes?
import bokeh.plotting
import bokeh.models
import bokeh.layouts
import numpy as np
# set up the interface
fig1 = bokeh.plotting.figure(x_range=(0, 10), y_range=(0, 10))
im1 = fig1.image([], dw=5, dh=5)
button = bokeh.models.Button(label='scramble')
# add everything to the document
bokeh.plotting.curdoc().add_root(bokeh.layouts.column(button, fig1))
# define a callback and connect it
def callback():
# this always works:
im1.data_source.data = {'image': [np.random.random((100,100))]}
# these changes only take effect after pressing the "Reset"
# button, or after triggering this callback function twice:
im1.glyph.update(x=1, y=1, dw=9, dh=9)
button.on_click(callback)
I don't immediately see why you code isn't work. I can suggest explicitly using a ColumnDataSource and linking all of the Image glyph properties to columns in that source. Then you should be able to update the source.data in a single line and have all of the updates apply.
Here's some incomplete sample code to suggest how to do that:
from bokeh.models import Image, ColumnDataSource
from bokeh.plotting import figure
# the plotting code
plot = figure()
source = ColumnDataSource(data=dict(image=[], x=[], y=[], dw=[], dh=[]))
image = Image(data='image', x='x', y='y', dw='dw', dh=dh)
plot.add_glyph(source, glyph=image)
# the callback
def callback():
source.data = {'image': [np.random.random((100,100))], 'x':[1], 'y':[1], 'dw':[9], 'dh':[9]}
button.on_click(callback)
I'd like to add some TeX text to my Qt form, like label - just text, no graph, no lines, no borders, just TeX. I thought something like this: render TeX to bitmap and then place that bitmap on form, e.g. into QLabel. Or even better - use some backend, add it to form and use something tex_label.print_tex(<tex code>). Seems matplotplot has TeX parsers, but I can't figure out how to use them...
Thanks.
As a variant:
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
# Get window background color
bg = self.palette().window().color()
cl = (bg.redF(), bg.greenF(), bg.blueF())
# Create figure, using window bg color
self.fig = Figure(edgecolor=cl, facecolor=cl)
# Add FigureCanvasQTAgg widget to form
self.canvas = FigureCanvasQTAgg(self.fig)
self.tex_label_placeholder.layout().addWidget(self.canvas)
# Clear figure
self.fig.clear()
# Set figure title
self.fig.suptitle('$TeX$',
x=0.0, y=0.5,
horizontalalignment='left',
verticalalignment='center')
self.canvas.draw()