This is kind of building on this thread (has been closed since): Importing additional JS resources to standalone html document - #2 by Bryan
Now this post is not about getting something to work, but that something I built works and I don’t know why/how! And I guarantee it’s simply knowing nothing about html/css, but if someone can explain this apparent voodoo to me it would greatly assist with my learning and understanding
What I’m trying to do:
Marry the standalone_embed.py example (as referred to by Bryan in the thread above) to my “save_html_wJSResources” function which provides me/other people building bokeh applications with what is essentially an “enhanced” bokeh.plotting.save function that allows for embedding of custom themes AND additional Javascript resources (for example d3.js as demo’ed in the example below).
Now the only “gotcha” to this is that there will be guaranteed cases where the person building the app wants to “manually override” the theme for just one model. Obviously since every layout is different there will be cases where individual models will need to be altered, font sizes shrunk to fit etc.
MRE/What I’ve built
from jinja2 import Template
from bokeh.io import curdoc
from bokeh.palettes import Spectral
from bokeh.plotting import figure, show
from bokeh.themes import Theme
from bokeh.embed import components
from bokeh.resources import Resources
#make a hypothetical theme
theme = Theme(json={'attrs':{
'Axis':{'axis_label_text_font':'arial'
,'axis_label_text_font_style':'bold'
,'axis_label_text_font_size':'14pt'
,'major_label_text_font':'arial'
,'major_label_text_font_style':'normal'
,'major_label_text_font_size':'10pt'
}
}
}
)
#dummy plot
x_f = [1.5, 2, 9]
y_f = [3, 3, 3.1]
p = figure(plot_width=400, plot_height=400)
p.line(x_f, y_f, line_width=3, color=Spectral[4][0])
p.axis[0].axis_label='testing'
p.axis[1].axis_label='something else'
#hard code override one of the axis label font sizes
p.axis[1].axis_label_text_font_size='25pt'
def save_html_wJSResources2(bk_obj,fname,resources_list_dict,html_title='Bokeh Plot',theme=None):
'''function to save a bokeh figure/layout/widget but with additional JS resources imported at the top of the html
resources_list_dict is a dict input of where to import additional JS libs/scripts so they can be utilized into CustomJS etc in bokeh work
e.g. {'sources':['http://d3js.org/d3.v6.js'],'scripts':[var_storing_jsscript]}
theme arg is to pass a custom theme
'''
if theme is None:
script, div = components(bk_obj)
else:
script, div = components(bk_obj, theme=theme)
template = Template('''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>'''+html_title+'''</title>
{{ resources }}
{{ script }}
</head>
<body>
<div>
{{ div }}
</div>
</body>
</html>
''')
resources = Resources().render()
for r in resources_list_dict['sources']:
resources = resources +'''\n<script type='text/javascript' src="'''+r+'''"></script>'''
for r in resources_list_dict['scripts']:
resources = resources +'''\n<script type='text/javascript'> '''+r+'''</script>'''
html = template.render(resources=resources,
script=script,
div=div)
print('writing '+fname+'...')
with open(fname, mode="w", encoding="utf-8") as f:
f.write(html)
save_html_wJSResources2(bk_obj=p,fname='Magic.html',resources_list_dict={'sources':['http://d3js.org/d3.v6.js'],'scripts':[]}
,html_title='Magic',theme=theme)
What I Expected to Happen
Since I apply the “manual override” “first” in the code and apply the theme when I save the html “after” (during the call to save_html_wJSResources2), I expected the theme to “overwrite” my manual update (basically the two axis labels would be the same font size (14pt).
What Actually Happens
The manual override actually works!
So what do I think is actually happening then?
From what I can tell, there has to be some sort of “default” setting that gets written based on the applied theme… but if a particular model has been “manually overridden” by the user, a switch internally is triggered, signalling that it has been “hard coded” by the user, and therefore the default no longer applies to it, and the applied theme leaves it alone? Am I thinking correctly about this?
Follow up questions:
- Was this sort of setup was completely intentional by the devs? and
- Is this kind of setup the defacto standard for templating stuff on web apps?
Follow up statement from me regardless of the answers I get to these questions:
- That is freaking clever and the devs are way smarter than me