Неблокирующая отрисовка и обновление графиков с помощью bokeh

    image

    Есть у меня один Python-скрипт с расчётами. Там был цикл примерно на 2000 итераций, каждая из которых считалась несколько минут.

    И решил я, чтобы ловчее отлаживать тот скрипт, выводить график кой-каких метрик от номера итерации. И как посчитается очередная итерация, так оный график и обновлять.

    Проще всего проделать это с помощью bokeh. Точнее, с помощью bokeh-сервера для отрисовки графиков. Как — сейчас расскажу.

    Сначала запускаем сервер: сервачок идёт из коробки вместе с самим bokeh, так что после pip install bokeh достаточно набрать в консоли bokeh serve — и сервер запущен.

    Зачем он нужен? А затем, чтобы показ графиков

    • не блокировал исполнение остального кода (ибо происходит в браузере, в отдельном процессе),
    • чтобы график реагировал на изменение размеров окна (или на свёртывание-развёртывание)
    • и чтобы при этом мы в любой момент могли этот график изменить как захотим, прямо из нашего же Python-процесса!

    Делается это примерно так:

    import time
    import sys
    from bokeh.plotting import figure
    from bokeh.client import pull_session
    from bokeh.models import ColumnDataSource
    
    # Перед запуском этого скрипта -- не забудь запустить сервер-отрисовщик, набрав в консоли bokeh serve
    # Please run "bokeh serve" in console before start!
    
    
    if __name__ == "__main__":
        # Создаём браузерную сессию (вкладку в браузере, где мы будем рисовать графики)
        session = pull_session()
    
        # Создаём т.н. документ, который будем показывать на сессии (фигуру с осями и графиками)
        fig = figure(title=("Total TBS (in bits)"), plot_height=300, plot_width=800)
    
        # Созадём кривую и пополняемый источник данных к ней
        datasource = ColumnDataSource(data={"x": [], "y": []})
        line = fig.line(x="x", y="y", source=datasource, line_width=2, legend=("Super dooper line from hell"))
    
        # Браузер откроет новую вкладку с пустыми осями
        session.show(fig)
    
        # Начинаем изменять состояние графика
        for i in range(10000):
    
            # Здесь  мы всего лишь добавляем к графику ещё одну точку. При изменении datasource от кривой кривая перерисуется
            # Вы можете изменить график и посильнее = )
            datasource.stream({"x": [i], "y": [i ** 2]})
    
            # Без вызова этого метода примерно через 30-40 изменений график в табе перестанет обновляться, будьте осторожны
            session.force_roundtrip()
    
    
    # Удачной отладки!
    

    Раньше мне тоже приходилось делать подобное, но предыдущие решения были, мягко говоря, не столь хороши. Чего я только за свою жизнь не перепробовал…

    Осторожно, мозговой балласт!
    Можно использовать matplotlib в неблокинующем режиме, вручную дёргая plt.draw() на каждой итерации. Правда, собственной обработки сообщений от GUI в неблокирующем режиме у matplotlib нету, и если окошко свёрнётся или закроется другим окном, то надо ждать следующей итерации, чтобы его перерисовали. Так себе костыль, но для отладки сойдёт.

    Можно по-негритянски генерировать картинку с графиком тем же matplotlib и дампить на диск. Тоже лютый костыль, но на безрыбье прокатит. Или на удалённой машине без графики.

    Можно сделать и по-крутому: воспользоваться PyQt, завернуть расчётный код в QObject, задвинуть его в отдельный поток, завернуть matplotlib-графики в QWidget (или воспользоваться графиками из Qt Data Visualizaion, или из PyQtGraph), соединить математику с графикой через сигнал со слотом, и будет счастье. Правда, на быстрое решение для отладки это слабо похоже, да и Qt учить надо, но я такое пару раз делал.

    Можно поднять в отдельным процессом маааленькое серверное приложение для отрисовки графиков (например, с помощью aiohttp + PyQt + PyQtGraph), к которому стучаться через REST API из главного процесса. Когда-то я делал и такое, но на быстрое-решение-для-отладки это тоже не тянуло.

    Можно писать в какую-нибудь БД (что там у нас сейчас в моде?), а потом напускать на это модную же Grafan’у. Правда, нужно ставить и БД, и Grafan’у, настраивать их, и вообще заморачиваться записью в БД. Через файл, наверно, тоже можно, но для двух графиков на тыщу точек каждый — это как из пушки по воробьям…

    Или можно разбираться в plotly.dash, выносить математику в отдельный поток, заворачивать в dash-приложение, и делать ещё чёртову уйму всякой фигни. Этого я уже не осилил, хотя и надо бы.

    Короче, удачной отладки!
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 5

      0
      Добрый день
      после выполнения команды bokeh serve появляется ошибка – User authentication hooks NOT provided (default user enabled).

      Подскажите, плз. как с ней разобраться (точного рецепта в Интернете не нашел).
        0
        А ОС какая?
          0
          Win 8.1
            0
            Странно, и у меня Винда. Правда, семёрка.

            А можете запустить bokeh serve --log-level=DEBUG

            и скопировать полный вывод консоли после этого?
              0
              после запуска команды bokeh serve как и прежде выдаёт:
              Starting Bokeh server version 1.4.0 (running on Tornado 6.0.3)
              User authentication hooks NOT provided (default user enabled)

              отладочная инфа (у меня система попросила всё писать в нижнем регистре):
              bokeh serve --log-level=debug

              выдаётся общее описание, но там мало чего для меня понятно — спишем на глюк моей системы. Подозреваю, что где-то в недрах винды нужно принудительно какой-то путь прописывать.

              Если всё-таки найду способ запуститься, дополню ветку.

      Only users with full accounts can post comments. Log in, please.