Есть у меня один 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 и дампить на диск. Тоже лютый костыль, но на безрыбьи и сойдёт. Или на удалённой машине без графики.
Можно сделать и по-крутому: воспользоваться PyQt, завернуть расчётный код в QObject, задвинуть его в отдельный поток, завернуть matplotlib-графики в QWidget (или воспользоваться графиками из Qt Data Visualizaion, или из PyQtGraph), соединить математику с графикой через сигнал со слотом, и будет счастье. Правда, на быстрое решение для отладки это слабо похоже, да и Qt учить надо, но я такое пару раз делал.
Можно поднять в отдельным процессом маааленькое серверное приложение для отрисовки графиков (например, с помощью aiohttp + PyQt + PyQtGraph), к которому стучаться через REST API из главного процесса. Когда-то я делал и такое, но на быстрое-решение-для-отладки это тоже не тянуло.
Можно писать в какую-нибудь БД (что там у нас сейчас в моде?), а потом напускать на это модную же Grafan’у. Правда, нужно ставить и БД, и Grafan’у, настраивать их, и вообще заморачиваться записью в БД. Через файл, наверно, тоже можно, но для двух графиков на тыщу точек каждый — это как из пушки по воробьям…
Или можно разбираться в plotly.dash, выносить математику в отдельный поток, заворачивать в dash-приложение, и делать ещё чёртову уйму всякой фигни. Этого я уже не осилил, хотя и надо бы.
Короче, удачной отладки!
Автор: Кролик