Apply different stylesheets to different elements of a template - python

I am embedding a bokeh chart in a Flask template. The app uses a master stylesheet, while the chart needs its own styles.
The master stylesheet leaks into the chart and changes its appearance. How do I apply different styles to different sections in a template so that only one style is active?
See my question on the Bokeh mailing list for more code and images.
This is the code of the template that pulls in the chart object. resources, script, and div are the elements of the object, generated by the charting library.
{% extends "base.html" %}
{% block content %}
<frame>
<head>
<meta charset='utf-8' />
<meta http-equiv='content-type' content='text/html; charset=utf-8' />
{{ resources|indent(4)|safe }}
{{ script|indent(4)|safe }}
</head>
<body>
{{ div|indent(4)|safe }}
</body>
</frame>
{% endblock %}

You don't, that's not how HTML/CSS works. All linked stylesheets apply to the entire document, Jinja templates don't factor into it. You use selectors to selectively apply CSS rules to HTML elements. Give the chart element an id, and write rules for that id.
<div id='my_chart'></div>
#my_chart {
}

Related

Adding a custom datepicker to bokeh standard text input

I'm trying to add a standard datepicker (like air datepicker) to a standard bokeh text input.
I added the JS references and I'm linking a class in the text input field like:
event = TextInput(title=u"Event Date", value=' ', css_classes=['datepicker-here'])
In theory, that should work, yet, it is not...
I also tried with other standard datepicker, to no success.
Here is another case: flatpicker.
A simple datepicker which works perfectly with this setup:
Create a simple text input:
event = TextInput(title=u"event", css_classes=['flatpkr'])
and put the required js code in the template.
flatpicker examples here (how it should work):
$("input[type='text']").flatpickr({
enableTime: true,
dateFormat: "F, d Y H:i"
});
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.2.3/flatpickr.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.2.3/themes/dark.css">
</head>
<body>
<div>
<input type="text" placeholder="Please select Date Time">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.2.3/flatpickr.js"></script>
</body>
If this is set in a plain webpage, everything is working properly. In Bokeh it does not, although I set exactly the same code in the template
{% extends base %}
{% block postamble %}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.2.3/flatpickr.css">
{% endblock %}
{% block contents %}
{{ super() }}
<!-- JS Scripts -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<!-- Flatpicker -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.2.3/flatpickr.js"></script>
<script type="text/javascript">
flatpickr("input[type='text']", {
"minDate": new Date().fp_incr(1)
});
</script>
{% endblock %}
The extra information confirms what I thought you intended, but is not actually enough to run and investigate. That said, contrary to your supposition "In theory, that should work", I would actually never expect this to work. And the reason is that declaring a Bokeh TextInput does mean "just dump a bare <input> in the DOM". Rather, there is an entire JS implementation and CSS that Bokeh provides to support its version of a text input widget. In general, I have zero confidence that it would ever "just work" to have two different JS libraries fighting to control the same DOM element in their own separate ways.
If you want to do this sort of thing (i.e. integrate outside widgets seamlessly with Bokeh), it's possible, but the usual mechanism is by Extending Bokeh. In particular see the example Adding a Custom Widget. A custom extension will have a Python object that represents it and that is automatically synchronized the same as any other Bokeh object.
Another (simpler) possibility: You could put HTML (including your plain <input>) in a Bokeh Div. Bokeh won't try to style or control any DOM elements added inside a Div. That might reasonably be expected to work (though I have not tried). A widget added this way will not have a Python counterpart that it is automatically synced with, but its not clear what your actual requirements are, so that might be fine.

Changing font-size of slider title in bokeh

I am trying to increase the font-size of the title of a slider in bokeh (0.12.4). For any other object there seems to be a "something_font_size" attribute, but as far as I can tell this doesn't seem to be the case for a slider title as it's saved as a string. Is there a way of doing this using bokeh or do I need to find a way to fix it using JavaScript?
Not certain if you can fix this using bokeh.
Try see what attributes and methods can be applied to the slider object.
If that fails, you could change it through investigating the elements on chrome/firefox then adding these to a styles.css file or similar.
.bk-slider-parent
{
font-size:20px
}
That seemed to increase both the name and value font sizes, you can also change just the values font size/color etc...
Let me just add that I am running with
bokeh serve --show Appdirectory
and Appdirectory contains main.py, Templates/index.html , Templates/styles.css - so you can include the snippet in either file (styles.css is included in index.html)
Updated: See below for bare minimum index.html should look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Title</title>
{{ bokeh_css }}
{{ bokeh_js }}
</head>
<body>
<div class="content">
<h1>Title</h1>
{{ plot_div|indent(8) }}
{{ plot_script|indent(8) }}
</div>
</body>
</html>

Adding Google Fonts to Flask

So with flask I know that I can add CSS with
<link ... href="{{ url_for('static', filename='stylesheets/style.css') }}" />
but if I am adding a google font which usually in HTML looks like
<link href='http://fonts.googleapis.com/css?family=PT+Sans:400,700' rel='stylesheet' type='text/css'>
what do I need to edit/add for it to work with Flask?
Add the google font link to your index.html or any page that you want like this:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename= 'mystyle.css') }}">
<link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300' rel='stylesheet' type='text/css'>
{% endblock %}
Then, add the CSS font statement to your custom CSS file (mine is static/mysyle.css) :
body {
font-family: 'Open Sans Condensed', sans-serif;`
}
You can't use static here like you would normally for a link to a resource on the webserver. Your link is still essentially static but not related to anything you are serving. So you either put the Google font link directly in the HTML template, or as a variable expanding to the full link (which would be convenient if you aren't using the same header template everywhere and may change the font later).

django render_to_response on google app engine

I am experiencing a weird issue using Django on Google App Engine. I have a file upload form defined within a django-app like this:
class ConvertForm(forms.Form):
from = forms.ChoiceField(choices=choices_from,
label='from:')
to = forms.ChoiceField(choices=choices_to,
label='to:')
class Meta:
fields = ('from','to')
And then I have in my app.views file the following:
def convert(request):
if request.POST:
form = ConvertForm(request.POST,request.FILES)
if form.is_valid():
from = form.cleaned_data['from']
to = form.cleaned_data['to']
# Redirect to a result page after post to avoid duplicates
return HttpResponseRedirect('/convert/results')
else:
form = ConvertForm()
args = {}
args.update(csrf(request))
args['form']=form
return render_to_response('convert.html',args)
The form-part of my convert.html template looks like this:
<form action="/convert/convert/" method="post" enctype="multipart/form-data">{%\
csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type="submit" name="submit" value="Convert">
</form>
It's supposed to be a file-upload form (hence the multipart), but I edited the form-contents for brevity.
Now, when I browse the proper url, nothing happens. I can clearly see that the correct functions are being called, since replacing the body of the convert() function with a simple
return render_to_response('convert_test.html',{'some_text':some_text})
displays the value of some_text in the browser window. Is there any additional quirks when dealing with forms within GAE that I am missing, or why isn't convert.html being rendered with the form? I should mention that all of this is on localhost, I haven't deployed it yet.
Edit: After some more fiddling around with this, it seems that maybe the source of the error is in the inheritance of templates. If I take out all django-tags {} in the convert.html, I can get the form to render correctly.
So, the question is now how do I properly set up template inheritance within GAE?
My full convert.html template looks like this:
{% extends "base.html" %}
{% block content %}
<h2>[ convert ]</h2>
<form action="/convert/convert/" method="post" enctype="multipart/form-data">{% \
csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type="submit" name="submit" value="Convert">
</form>
{% endblock %}
So basically redefining the content block of the base.html template. This was working perfectly before I tested it on GAE, so I can't shake the feeling that's involved somehow.
If it is relevant, my django settings.py looks like this for the templates:
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__),'..','convert','templates'),
os.path.join(os.path.dirname(__file__),'..','templates'),
)
And as I said, taking out the {}-tags from convert.html gives me the form, but rendered on its own in an otherwise completely white and empty page.
This is the contents of my base.html template
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/\
DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="Your description goes here" />
<meta name="keywords" content="your,keywords,goes,here" />
<meta name="author" content="Your Name" />
<link rel="stylesheet" type="text/css" href="/static/css/basic-minimal.css" ti\
tle="Basic Minimal" media="all" />
<title>Basic Minimal v1.2</title>
</head>
<body class="light">
<div id="wrap">
<div id="header">
<h1>[ snazzy title ]</h1>
</div>
<div id="sidebar">
<ul>
<li>[ Convert ]</li>
<li>[ Transform ]</li>
<li>[ Help ]</li>
<li>[ References ]</li>
</ul>
</div>
<div id="content">
<h2>[ more title ]</h2>
<p>Text</p>
<p>More text</p>
</div>
</div>
</body>
</html>
This works beautifully for the "title" page (even the css gets loaded), but rendering other templates as convert.html above using this as a base does not work.

How would I modify jinja2 to keep CSS and JavaScript with the corresponding HTML in included files?

This is my first question ever on SO and I'm happy to take any tips on improving. Be gentle :)
To increase maintainability, I am trying to keep CSS, JS and HTML all together in the same file. This becomes difficult with include files, or macros, etc. The final output template.render() should grab all that CSS and JS, and place it at the top and bottom of the base HTML template.
Example should explain what I mean:
base.html
<html>
<head>
<style>
{{ print_css() }}
</style>
</head>
<body>
{% include 'bolded_text.html' %}
<script>
{{ print_js() }}
</script>
</body>
</html>
bolded_text.html
{# this doesn't need to be a filter. It could be a block, or anything else that would make this work #}
{% filter add_css %}
strong {
background: #ccc;
}
{% endfilter %}
<strong>Bolded text</strong>
{% filter add_js %}
alert('javascript included!');
{% endfilter %}
Output would of course be:
<html>
<head>
<style>
strong {
background: #ccc;
}
</style>
</head>
<body>
{% include 'bolded_text.html' %}
<script>
alert('javascript included!')
</script>
</body>
</html>
Predictably, the problem is that print_css() outputs nothing.
I tried doing this with context filters, where add_js and add_css append to a variable in the context, and then print_css and print_js output that variable. This works nicely and simply for JavaScript, since the print statement follows all the filters.
However, I can't think of a way to make it work with CSS. If I could make the print_css() call lazy, or swap in the output in the jinja2 AST somehow, that might work. I'm trying to avoid hacky string manipulation (such as outputting the CSS at the bottom and then using regexes to move it to the top).
I figured there might be a way to do this elegantly and my experience with jinja2's low-level API is limited.
Any help is much appreciated, thanks!
Macros will do that, but I don't see the point why you wouldn't use StyleSheets, this enables the browser a way better caching and it's also easier to maintain.
EDIT:
Example:
{% macro generate_css(strong_bg='#ccc', additional_options=None) -%}
strong {
background: {{ strong_bg }};
}
{%- endmacro %}
<head>
<style>
{{ generate_css() }}
</style>
</head>

Categories