Принудительно обновлять виджеты bokeh? - PullRequest
0 голосов
/ 18 марта 2020

Я новичок в bokeh и пишу небольшое серверное приложение bokeh, в котором есть сюжет и кнопка. Когда кнопка нажата, данные пересчитываются и график обновляется. Идея состоит в том, что, как только кнопка нажата, она меняет цвет и метку, также появляется текст «вычисление ...». Когда вычисления завершены, график обновляется, и текст исчезает.

Однако, когда кнопка нажата, она не меняет цвет, и текст не отображается до того, как вычисления будут выполнены (это занимает несколько секунд). Все это обновление виджета происходит после расчетов. Вопрос, возможно ли принудительно обновить виджет, например flush = True в случае print () или что-то подобное может быть?

Я не смог найти ничего в документации боке. Я также попытался разделить изменения виджетов и вычисления и выполнить их в двух отдельных функциях, но это не помогло. Установка задержки между сменой кнопки и вызовом функции расчета также не помогла. Кажется, что обновление на виджетах происходит только при выходе из функции обратного вызова или даже позже. Единственное, что я не проверял, это Custom JS, но я не знаю, как написать js код для обновления кнопки.

Спасибо за любую помощь!

Вот пример кода, близкий к тому, что я на самом деле использую:

from bokeh.plotting import figure
from bokeh.models import Button, PreText, ColumnDataSource
from bokeh.layouts import row

p = figure()
source = ColumnDataSource(data={"x":[0], "y":[0]})
p.line(x="x", y="y", source=source)
variable = False

# initialise widgets
switchButton = Button(label='Anticrossing OFF', button_type="default")
process_markup = PreText(text='Calculating...', visible=False)

def callback(arg):
    global variable
    global process_markup

    variable = not variable

    # change button style
    if variable:
        switchButton.update(label = 'Anticrossing ON',
                              button_type = 'success')
    else:
        switchButton.update(label = 'Anticrossing OFF',
                              button_type = 'default')
    # show "calculating..."
    process_markup.update(visible=True)

    # do long calculations
    x, y = calculate_data(variable)
    source.data = {"x":x, "y":y}

    # hide "calculating..."
    process_markup.update(visible=False)

switchButton.on_click(callback)
col = column(switchButton, process_markup)
curdoc().add_root(row(col, p))

1 Ответ

0 голосов
/ 18 марта 2020

Bokeh может отправлять обновления данных только тогда, когда элемент управления возвращается обратно к событию сервера l oop. В вашем случае вы выполняете вычисления без каждого элемента управления, поэтому он отправляет все обновления, когда обратный вызов уже выполнен.

Самое простое, что нужно сделать в вашем случае, это разделить обратный вызов на блоки, которые требует синхронизации и запускает каждый блок в обратном вызове следующего тика:

def callback(arg):
    global variable
    global process_markup

    variable = not variable

    # change button style
    if variable:
        switchButton.update(label = 'Anticrossing ON',
                              button_type = 'success')
    else:
        switchButton.update(label = 'Anticrossing OFF',
                              button_type = 'default')
    # show "calculating..."
    process_markup.update(visible=True)

    def calc():
        # do long calculations
        x, y = calculate_data(variable)
        source.data = {"x":x, "y":y}

        # hide "calculating..."
        process_markup.update(visible=False)

    curdoc().add_next_tick_callback(calc)

Обратите внимание, однако, что такое решение подходит только в том случае, если вы единственный пользователь и вам не нужно ничего делать, пока вычисление выполняется Бег. Причина в том, что вычисления блокируются - вы не можете общаться с Bokeh каким-либо образом во время его работы. Правильное решение потребует асин c, например, потоков. Для получения более подробной информации, ознакомьтесь с разделом Обновление из потоков Руководства пользователя Bokeh.

...