For a rather elaborate web app I’m working on, it would be really convenient if users could change the value of a TextInput field by pressing a key on the keyboard. I can get the value of TextInput to change, but then this is not registered and passed on. If I manually change the value it does pass on, so I suppose I’m missing some trigger when I use the JS event listener but I can’t figure it out.
Can this be done? Or are there perhaps less elaborate ways to pass values from the keyboard on as well? I’ve sunk so much time in this, please help
Minimal example (Bokeh 1.4.0, python 3.7):
main.py
from bokeh.layouts import column
from bokeh.models.widgets import Div, TextInput
from bokeh.plotting import curdoc
def change_div(attr, old, new):
if old == new: return
div.text = new
div = Div(text="0")
ti_obj = TextInput(value='0', name='text_input1')
ti_obj.on_change('value_input', change_div)
curdoc().add_root(column(ti_obj, div))
and index.html
{% block css_resources %}
{{ bokeh_css }}
{% endblock %}
{%block js_resources %}
{{ bokeh_js }}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-1.4.0.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-tables-1.4.0.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-gl-1.4.0.min.js"></script>
{% endblock %}
{%block body %}
<body>
{% block inner_body %}
{% block contents %}
{% for doc in docs %}
{{ embed(doc) if doc.elementid }}
{% for root in doc.roots %}
{{ embed(root) | indent(10) }}
{% endfor %}
{% endfor %}
{% endblock %}
{{ plot_script | indent(8) }}
<script>
var tst = '';
document.addEventListener('keydown', (e) => {
console.log('key pressed: ' + e.keyCode);
if (isFinite(e.key)) {document.getElementsByName("text_input1")[0].value = e.key;}
});
</script>
{% endblock %}
</body>
{% endblock %}
So with file structure:
- my_app
- main.py
- templates
- index.html
I would run with
bokeh serve --show my_app
EDIT:
So I think the issue is that I change the HTML attribute but not the value attribute of the bokehJS object, as explained here: jquery - How can I set the value of an input text field in Bokeh from JavaScript? - Stack Overflow
I tried to find the corresponding field in the bokeh object by manually inspecting the tree of window.Bokeh.index, but I can’t find the proper field in the bokeh object…
EDIT 2:
Solved it! Took some digging, but the correct property was in window.Bokeh.documents[0]._all_models_by_name._dict[‘text_input1’].value_input. So this works with small modification to index.html:
{% extends base %}
{% block css_resources %}
{{ bokeh_css }}
{% endblock %}
{%block js_resources %}
{{ bokeh_js }}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-1.4.0.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-tables-1.4.0.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-gl-1.4.0.min.js"></script>
{% endblock %}
{%block body %}
<body>
{% block inner_body %}
{% block contents %}
{% for doc in docs %}
{{ embed(doc) if doc.elementid }}
{% for root in doc.roots %}
{{ embed(root) | indent(10) }}
{% endfor %}
{% endfor %}
{% endblock %}
{{ plot_script | indent(8) }}
<script>
var tst = '';
document.addEventListener('keydown', (e) => {
console.log('key pressed: ' + e.keyCode);
if (isFinite(e.key)){
window.Bokeh.documents[0]._all_models_by_name._dict['text_input1'].value_input = e.key;}
});
</script>
{% endblock %}
</body>
{% endblock %}
You can use this to send keystrokes to the server side in general!