Updating widgets and force refreshing the interface

Hi,

I try to build a Bokeh server where I want to start a time consuming computation when pressing a button. This computation depends on some inputs which are specified by the user via some widgets. During the computation (and when presenting the results) I want to disable the input widgets such that it is clear to which inputs the result belongs to. Furthermore I would like to present some status information about the computations. However, the web interface does not seem to be updated until the callback function is completed.

I build a minimal example demonstrating my problem (the computation time is simulated by a “time.sleep(1)” call). It is at the end of this post.

What do I want to have?
When pressing the button I want that the select widget and the button are disabled immediately. Also the label of the button should change to “Step 1”. After one second the button should have the label “Step 2” and - again one second later - the button should be enabled and it should be labeled with “Press to enable”.

What does the code?
If you press the button, nothing happens for two seconds (during this time you can also press the button again or select another value in the select widget -> that’s exactly what I do not want to have). Afterwards the select widget gets disabled and the button gets the label “Press to enable”.

It seems that all changes regarding the interface are omitted until the callback function returns (or these changes are processed at this time). Is there a way to force refreshing the interface?

I hope that I could explain my problem clearly. When you have any solutions (or at least ideas) I would be happy to hear them.

Thanks,
Tim

===== Example Code =====

import os
import time

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models.widgets import Button, Select

s = Select(title="Selection",
           options=["Option A", "Option B"],
           value="Option A")

b = Button(label="Press")

def press_button():
    if s.disabled:
        s.disabled = False
        b.label = "Press"
    else:
        s.disabled = True
        b.disabled = True
        b.label = "Step 1"
        time.sleep(1)
        b.label = "Step 2"
        time.sleep(1)
        b.label = "Press to enable"
        b.disabled = False

b.on_click(press_button)

layout = column(s, b)
curdoc().add_root(layout)
curdoc().title = "Refresh document"

@TimB For future reference please format the code as text with triple backtick ``` fences around code blocks (or use the code formatting button </> in the UI)


Bokeh state only synchronizes with the browser when the callback ends . If you want to do some update, then alot of blocking work, then another update, you will need to split things up so the first callback completes immediately then schedules the rest of the work to happen after the return. The simplest way is with add_next_tick_callback :

from time import sleep
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Button, Div

d = Div(text="start")

b = Button()

def work():
    sleep(2)
    d.text = "end"

def cb():
    d.text = "middle"
    curdoc().add_next_tick_callback(work)

b.on_click(cb)

curdoc().add_root(column(d, b))

If you want to do several steps, you will need to chain these together with each callback adding the next.

1 Like

Works perfectly. Thank you very much for the fast answer!

1 Like