I am trying to change the Button label text from Javascript. Here is a minimal program that demonstrates what I am trying to do. I did succeed in changing the label text, but I have been told this is not the right way to do it.
This is how I ended up changing the label, which is touching the button object properties directly,
el[0].firstChild.childNodes[0].innerHTML = "new label";
This path changed from a previous version of Bokeh, and may change again, so this is NOT the right way to change the label, hence this post.
Full Code,
#!/usr/bin/python
# -*- coding: utf-8 -*-
import queue
import time
import threading
from flask import Flask
from flask import request, jsonify, Response
from bokeh.layouts import layout, row
from bokeh.models.widgets.buttons import Button
from bokeh.models.callbacks import CustomJS
from bokeh.embed import components
from dominate.tags import *
import dominate
from dominate.util import raw
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
q = queue.Queue()
app = Flask(__name__)
PAGE_URL = "/"
TEST_PORTAL_BUTTON = "/button"
TEST_PORTAL_LISTENER = "/stream"
def dominate_document(title="pywrapBokeh", bokeh_version='1.2.0'):
d = dominate.document(title=title)
with d.head:
link(href="https://cdn.pydata.org/bokeh/release/bokeh-{bokeh_version}.min.css".format(
bokeh_version=bokeh_version),
rel="stylesheet",
type="text/css")
script(src="https://cdn.pydata.org/bokeh/release/bokeh-{bokeh_version}.min.js".format(
bokeh_version=bokeh_version))
link(href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-{bokeh_version}.min.css".format(
bokeh_version=bokeh_version),
rel="stylesheet",
type="text/css")
script(src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-{bokeh_version}.min.js".format(
bokeh_version=bokeh_version))
link(href="https://cdn.pydata.org/bokeh/release/bokeh-tables-{bokeh_version}.min.css".format(
bokeh_version=bokeh_version),
rel="stylesheet",
type="text/css")
script(src="https://cdn.pydata.org/bokeh/release/bokeh-tables-{bokeh_version}.min.js".format(
bokeh_version=bokeh_version))
script(src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js")
meta(charset="UTF-8")
js = """
var eventSource;
$(document).ready(function(){{
var eventSource = new EventSource("{url}");
eventSource.onmessage = pageEventHandler;
}});
function pageEventHandler(e) {{
//alert(e.data);
console.log(e.data);
if (e.data.startsWith("b1")) {{
el = document.getElementsByClassName("b1_css");
console.log(el);
//
// How to properly set the Button Label???
el[0].firstChild.childNodes[0].innerHTML = e.data;
}}
}};
""".format(url=TEST_PORTAL_LISTENER)
with d.body:
script(raw(js))
return d
@app.route(PAGE_URL, methods=['GET', 'POST'])
def hello():
d = dominate_document(title="Test Java Buttons")
doc_layout = layout()
b1 = Button(label="START", width=100, css_classes=["b1_css"])
on_click = CustomJS(code="""$.getJSON('{}', function(data) {{ }}); """.format(TEST_PORTAL_BUTTON + '?button={}'.format("b1")))
b1.js_on_click(on_click)
doc_layout.children.append(row(b1))
_script, _div = components(doc_layout)
d.body += raw(_script)
d.body += raw(_div)
return "{}".format(d)
@app.route(TEST_PORTAL_BUTTON, methods=['GET', 'POST'])
def url__test_button():
logger.info("You Pressed: {}".format(request.args))
d = {"success": True, "msg": None, "from": "url__test_button"}
return jsonify(d)
@app.route(TEST_PORTAL_LISTENER, methods=['GET'])
def url__test_stream():
def eventStream():
while True:
# wait for source data to be available, then push it
yield """data: {}\n\n""".format(get_message())
return Response(eventStream(), mimetype="text/event-stream")
def get_message():
try:
item = q.get(block=True)
logger.info("forwarding {}".format(item["type"]))
return item["type"]
except queue.Empty:
return "None"
except Exception as e:
logger.exception("Error processing event: %s" % e)
def send_message():
count = 0
while True:
time.sleep(2)
count += 1
d = {"type": "b1 {}".format(count)}
logger.info("Sending {}".format(d))
q.put(d)
t = threading.Thread(target=send_message)
t.start()
app.run(host="0.0.0.0", port=6800, debug=False)
As a side note, this code is an example of how to send data between python and the web page with Flask. The python side is sending a new button label every 2s, and when the button is pressed, it calls a python function (via flask url). While I was trying to change the button label, sometimes modifying a button property would break some of these functions, so this is a complicated example to make sure any solution doesn’t break those behaviours.
Is it possible, when making the customJS()
for the button, that a function can be added so that the label can be changed?