bokeh server app returns blank screen - python-3.x

I am trying to render a simple app on bokeh server. I took the script straight from the bokeh website.
# myapp.py
from random import random
from bokeh.layouts import column
from bokeh.models import Button
from bokeh.palettes import RdYlBu3
from bokeh.plotting import figure, curdoc
# create a plot and style its properties
p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None)
p.border_fill_color = 'black'
p.background_fill_color = 'black'
p.outline_line_color = None
p.grid.grid_line_color = None
# add a text renderer to our plot (no data yet)
r = p.text(x=[], y=[], text=[], text_color=[], text_font_size="20pt",
text_baseline="middle", text_align="center")
i = 0
ds = r.data_source
# create a callback that will add a number in a random location
def callback():
global i
# BEST PRACTICE --- update .data in one step with a new dict
new_data = dict()
new_data['x'] = ds.data['x'] + [random()*70 + 15]
new_data['y'] = ds.data['y'] + [random()*70 + 15]
new_data['text_color'] = ds.data['text_color'] + [RdYlBu3[i%3]]
new_data['text'] = ds.data['text'] + [str(i)]
ds.data = new_data
i = i + 1
# add a button widget and configure with the call back
button = Button(label="Press Me")
button.on_click(callback)
# put the button and plot in a layout and add to the document
curdoc().add_root(column(button, p))
I run this in my Win Python Command Prompt.exe using the below:
python -m bokeh serve --show main.py
This runs the app, but I am left with a blank webpage. See the output in the command prompt below:
Can anyone help with how to get this to show the doc?

Related

Holoviews heatmap animation labelling slider

I have created an animated heatmap using holoviews with the 'bokeh' renderer. The code is below and runs in a Jupyter Notebook. Basically I compose a dictionary of heatmaps then I use 'hv.DynamicMap' and 'hv.streams' to create the animation ie. stream keys of dict and render the associated heatmap.
The code runs successfully as an animation but produces an output where a single frame looks like this:
I have two questions:
I want the slider to show the 'key' of the dictionary not just the
index of the list of keys as it does now. So in the image above 'a0' not '0'. How can I do that?
there are warnings being thrown about 'fixed_width'. I have set the
height and width of all the objects so why is there a warning?
import numpy as np
import holoviews as hv
from bokeh.io import show, curdoc, output_notebook
from bokeh.layouts import layout
from bokeh.models import Slider, Button
renderer = hv.renderer('bokeh').instance(mode='server')
output_notebook()
# generate a dict of heatmaps
heatmap_dict = {}
for i in range(10):
heatmap = hv.HeatMap((np.random.randint(0, 10, 100), np.random.choice(['A', 'B', 'C', 'D', 'E'], 100),
np.random.randn(100), np.random.randn(100)), vdims=['z', 'z2']).sort().aggregate(function=np.mean)
heatmap.opts(height=400, width=400)
heatmap_dict['a' + str(i)] = heatmap
heatmap_keys = list(heatmap_dict.keys())
# Create the holoviews app again
def mapping(phase):
key = heatmap_keys[phase]
return heatmap_dict[key]
stream = hv.streams.Stream.define('Phase', phase=0)()
dmap = hv.DynamicMap(mapping, streams=[stream])
# Define valid function for FunctionHandler
# when deploying as script, simply attach to curdoc
def modify_doc(doc):
# Create HoloViews plot and attach the document
hvplot = renderer.get_plot(dmap, doc)
# Create a slider and play buttons
def animate_update():
year = slider.value + 1
if year > end:
year = start
slider.value = year
def slider_update(attrname, old, new):
# Notify the HoloViews stream of the slider update
stream.event(phase=new)
start, end = 0, len(heatmap_keys) - 1
slider = Slider(start=start, end=end, value=start, step=1, title="Phase", height=30, width=180)
slider.on_change('value', slider_update)
callback_id = None
def animate():
global callback_id
if button.label == '► Play':
button.label = '❚❚ Pause'
callback_id = doc.add_periodic_callback(animate_update, 50)
else:
button.label = '► Play'
doc.remove_periodic_callback(callback_id)
button = Button(label='► Play', width=60, height=30)
button.on_click(animate)
# Combine the holoviews plot and widgets in a layout
plot = layout([
[hvplot.state],
[slider, button]], sizing_mode='fixed')
doc.add_root(plot)
return doc
# To display in the notebook
show(modify_doc, notebook_url='localhost:8888')

Can't get Bokeh server running with DirectoryHandler

I'm trying to get a bokeh server running with a DirectoryHandler.
The server seems to start but when I load the page in my browser it shows a blank page.
When I use a ScriptHandler and call the main.py directly everything works fine. The problem is that in this case the static directory is not recognized.
This does not work (DirectoryHandler):
from bokeh.application import Application
from bokeh.application.handlers import DirectoryHandler
from bokeh.server.server import Server
from tornado.ioloop import IOLoop
io_loop = IOLoop.current()
def create_bokeh_server(io_loop, file, port):
"""Start bokeh server with applications paths"""
bokeh_app = Application(DirectoryHandler(filename=file))
# kwargs lifted from bokeh serve call to Server, with created io_loop
kwargs = {
'io_loop': io_loop,
'port': port,
}
server = Server(bokeh_app,**kwargs)
return server
if __name__ == '__main__':
# create server
print('Create server')
bokeh_server = create_bokeh_server(io_loop=io_loop, file='C:/bokeh_app_dir/', port=8080)
# start bokeh server
print('Start bokeh server')
bokeh_server.start()
# start web server
print('Start Localhost')
io_loop.start()
This does work(ScriptHandler)
from bokeh.application import Application
from bokeh.application.handlers.script import ScriptHandler
from bokeh.server.server import Server
from tornado.ioloop import IOLoop
io_loop = IOLoop.current()
def create_bokeh_server(io_loop, file, port):
"""Start bokeh server with applications paths"""
bokeh_app = Application(ScriptHandler(filename=file))
# kwargs lifted from bokeh serve call to Server, with created io_loop
kwargs = {
'io_loop': io_loop,
'port': port,
}
server = Server(bokeh_app,**kwargs)
return server
if __name__ == '__main__':
# create server
print('Create server')
bokeh_server = create_bokeh_server(io_loop=io_loop, file='C:/bokeh_app_dir/main.py', port=8080)
# start bokeh server
print('Start bokeh server')
bokeh_server.start()
# start web server
print('Start Localhost')
io_loop.start()
where main.py is:
rom random import random
from bokeh.layouts import column
from bokeh.models import Button
from bokeh.models.widgets import CheckboxGroup, RadioGroup
from bokeh.palettes import RdYlBu3
from bokeh.plotting import figure, curdoc
# create a plot and style its properties
p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None)
p.border_fill_color = 'black'
p.background_fill_color = 'black'
p.outline_line_color = None
p.grid.grid_line_color = None
# add a text renderer to our plot (no data yet)
r = p.text(x=[], y=[], text=[], text_color=[], text_font_size="20pt",
text_baseline="middle", text_align="center")
i = 0
ds = r.data_source
# create a callback that will add a number in a random location
def callback():
global i
# BEST PRACTICE --- update .data in one step with a new dict
new_data = dict()
new_data['x'] = ds.data['x'] + [random()*70 + 15]
new_data['y'] = ds.data['y'] + [random()*70 + 15]
new_data['text_color'] = ds.data['text_color'] + [RdYlBu3[i%3]]
new_data['text'] = ds.data['text'] + [str(i)]
ds.data = new_data
i = i + 1
def update():
"""Example of updating plot on radio button change"""
if radio_group.active == 0:
p.border_fill_color = 'black'
p.background_fill_color = 'black'
elif radio_group.active == 1:
p.border_fill_color = 'white'
p.background_fill_color = 'white'
else:
p.border_fill_color = 'blue'
p.background_fill_color = 'blue'
# add a button widget and configure with the call back
button = Button(label="Press Me")
button.on_click(callback)
# add group of radio butt
radio_group = RadioGroup(
labels=['black', 'white', 'blue'],
active=0
)
radio_group.on_change('active', lambda attr, old, new: update())
# put the button and plot in a layout and add to the document
curdoc().add_root(column(radio_group, button, p))
What am I missing here?
I need the static directory for images I want to load.
Good working example! I am struggling to recreate though. I ran your code exactly on Windows, Python3.7 and bokeh 1.3.1 and bokeh server launches the app on http://localhost:8080/.
Some things to try:
Where and how are you executing the calling script (the scripts where the only difference is DirectoryHandler or ScriptHandler)? I put mine in a separate run.py and just ran it with "python run.py". run.py can but doesn't have to be in the top level app folder. It runs with main.py as the only file in my top level app folder.
What are outputs of python console logging?
What are outputs of your browsers developer tab (usually F12)? I do get an error that Tornado can't find the favicon.ico file (lil browser thumbnail image)
If you take a look at the Bokeh library code for the DirectoryHandler it doesn't do much beyond decorating the ScriptHandler!
https://docs.bokeh.org/en/latest/_modules/bokeh/application/handlers/directory.html#DirectoryHandler

How to link axes of all plots in a Bokeh layout?

I am designing a Bokeh layout using the Bokeh server. I am defining two main columns (see attached image), and I am attempting to link the x-axis of all plots on the right column. The problems are that:
I am trying to make this app as dynamic as possible, which mean that depending on the case-study, not all the plots will be available, and each individual plot is set from a separate function
Each plot object is stored in a list, and I don't know how to access their properties
The reference plot is not known a priori so I don't see how I can implement the example in the Bokeh doc - in other words, I need to first plot all the subplots to then get the relevant x_range
So I was wondering if it is possible to set the linking behaviour a posteriori once all plots in the column are defined (i.e. the output of plotDataset below). My intuition is to loop through the objects, get the children and set the x_range to the first plot, but I don't know how to do that.
Below is a simplified version of what I am trying to achieve. Ideally, I would get the x_range of the first plot of fCol and apply it to all other plots just before return column(fCol)
Any idea is greatly appreciated! And also, I am fairly beginner with Python so please shout if you see anything else horrible!
Thank you
def plotTS(data, col):
tTmp = []
# A loop that defines each tab of the plot
for i in range(len(col)):
fTmp = figure()
fTmp.circle(data[:]['time'], data[:][col[i]], color=color)
# Append tab
tTmp.append(Panel(child=fTmp))
# Return the tabs
return Tabs(tabs=tTmp)
def plotDataset(data):
col = ['NDVI', 'EVI'] # Name of the tabs
fCol = []
fCol.append(plotTS(data, col))
# NOTE: I use an append approach because in reality plotTS is called more than once
return column(fCol)
# General layout - I did not include the code for the left column
layout = row(leftColumn, plotDataset(data))
Link to image
See code below (Bokeh v1.1.0).
from bokeh.models import Panel, Tabs, Column, Row
from bokeh.plotting import figure
from tornado.ioloop import IOLoop
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
def modify_doc(doc):
leftColumn = Column(figure())
def plotTS(data, col):
tTmp = []
for i in col:
fTmp = figure()
fTmp.circle(data['x'], data['y'], color='black')
tTmp.append(Panel(child=fTmp, title = i))
return Tabs(tabs=tTmp)
def plotDataset(data):
col = ['NDVI', 'EVI']
fCol = plotTS(data, col)
shared_range = None
for panel in fCol.tabs:
fig = panel.child
if shared_range is None:
shared_range = fig.x_range
else:
fig.x_range = shared_range
return Column(fCol)
layout = Row(leftColumn, plotDataset(data = dict(x = [1, 2, 3], y = [1, 2, 3])))
doc.add_root(layout)
io_loop = IOLoop.current()
server = Server(applications = {'/app': Application(FunctionHandler(modify_doc))}, io_loop = io_loop, port = 5002)
server.start()
server.show('/app')
io_loop.start()

How do I implement a start-stop button / play-resume button for an animation in bokeh?

I am trying to implement a star-stop button for a bokeh animation. I am using bokeh server in combination with the curdoc() function, but so far I haven't had much success.
I am wondering how someone with more experience would do that?
thanks
Here you go. Run with bokeh serve --show app.py (tested on Bokeh v1.0.4)
from bokeh.models import ColumnDataSource, Toggle, Column
from bokeh.plotting import figure, curdoc
from datetime import datetime
import random
source = ColumnDataSource(dict(time = [datetime.now()], value = [random.randint(5, 10)]))
plot = figure(plot_width = 1200, x_axis_type = 'datetime', tools = 'pan,box_select,crosshair,reset,save,wheel_zoom')
plot.line(x = 'time', y = 'value', line_color = 'black', source = source)
toggle = Toggle(label = "Toggle", button_type = "success")
def update():
if toggle.active:
source.stream(dict(time = [datetime.now()], value = [random.randint(5, 10)]))
curdoc().add_root(Column(plot, toggle))
curdoc().add_periodic_callback(update, 1000)
Result:

Not able to click 3d objects and move them

I want to click on the loaded models and move them around. I used the code from chess sample examples and panda 3d tutorial without any success. Can someone figure out whats wrong with the code.
Thanks
from math import pi, sin, cos
import sys
from direct.showbase.ShowBase import ShowBase
import direct.directbase.DirectStart
from direct.task import Task
from panda3d.core import TextNode
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import CollisionTraverser, CollisionNode
from panda3d.core import CollisionHandlerQueue, CollisionRay
from panda3d.core import Point3, Vec3, Vec4, BitMask32
from direct.showbase.DirectObject import DirectObject
from panda3d.core import AmbientLight, DirectionalLight, LightAttrib
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# quit when esc is pressed
self.accept('escape', sys.exit)
#base.disableMouse()
# load the box model
self.box = self.loader.loadModel("models/xbox")
self.box.reparentTo(camera)
self.box.setScale(2.0, 2.0, 2.0)
self.box.setPos(8, 50, 0)
self.keyMap = {
"w" :False ,
"s" :False,
"a": False,
"d": False,
"mouse1": False,
"mouse3": False,
}
# CollisionTraverser and a Collision Handler is set up
self.picker = CollisionTraverser()
self.pq = CollisionHandlerQueue()
self.pickerNode = CollisionNode('mouseRay')
self.pickerNP = camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
self.box.setCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.picker.addCollider(self.pickerNP, self.pq)
self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
self.accept("mouse1", self.setKey, ["mouse1", True])
def mouseTask(self,task):
# check if we have access to the mouse
if base.mouseWatcherNode.hasMouse():
# get the mouse position
mpos = base.mouseWatcherNode.getMouse()
# set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
self.picker.traverse(render)
# if we have hit something sort the hits so that the closest is first and highlight the node
if self.pq.getNumEntries() > 0:
self.pq.sortEntries()
pickedObj = self.picker.getEntry(0).getIntoNodePath()
def setKey(self,key,value):
self.keyMap[key] = value
app = MyApp()
app.run()
I was just trying to do the same thing, when I found your question.
Thanks for your code, it help me to start!
I've manage to get it working :)
Just a remark: you use a task, with no return, this make the task run once.
You should have used: return task.cont
Anyway, here my working code for panda3d devel (1.8.0+):
import sys
from direct.showbase.ShowBase import ShowBase
from pandac.PandaModules import *
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# quit when esc is pressed
self.accept('escape',sys.exit)
#base.disableMouse()
# load the box model
box = self.loader.loadModel("models/box")
box.reparentTo(render)
box.setScale(2.0, 2.0, 2.0)
box.setPos(8, 50, 0)
panda = base.loader.loadModel("models/panda")
panda.reparentTo(render)
panda.setPos(0, 10, 0)
panda.setScale(0.1, 0.1, 0.1)
cNodePanda = panda.attachNewNode(CollisionNode('cnode_panda'))
cNodePanda.node().addSolid(CollisionSphere(0,0,5,5))
cNodePanda.show()
# CollisionTraverser and a Collision Handler is set up
self.picker = CollisionTraverser()
self.picker.showCollisions(render)
self.pq = CollisionHandlerQueue()
self.pickerNode = CollisionNode('mouseRay')
self.pickerNP = camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
box.setCollideMask(BitMask32.bit(1))
panda.setCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.picker.addCollider(self.pickerNP,self.pq)
self.accept("mouse1",self.mouseClick)
def mouseClick(self):
print('mouse click')
# check if we have access to the mouse
if base.mouseWatcherNode.hasMouse():
# get the mouse position
mpos = base.mouseWatcherNode.getMouse()
# set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode,mpos.getX(),mpos.getY())
self.picker.traverse(render)
# if we have hit something sort the hits so that the closest is first and highlight the node
if self.pq.getNumEntries() > 0:
self.pq.sortEntries()
pickedObj = self.pq.getEntry(0).getIntoNodePath()
print('click on ' + pickedObj.getName())
app = MyApp()
app.run()

Resources