2025, Nov 11 03:01

Обработка клика в NiceGUI и Plotly: индекс точки в Python

Разбираем, как в NiceGUI и Plotly корректно обработать plotly_click: принять событие, достать индекс точки из e.args и сохранить его в Python. Примеры кода.

Работа с пользовательскими действиями в графике NiceGUI + Plotly часто начинается с простого вопроса: как получить точку, по которой кликнули, и использовать её в Python, а не просто показывать уведомление? Встроенный пример с ui.notify хорош для быстрой проверки события, но следующий шаг — присвоить индекс выбранной точки переменной и что-то с ним сделать. Давайте разберёмся, почему наивный обработчик может не сработать и как корректно извлечь индекс точки из полезной нагрузки события.

Воспроизведение проблемы

Предположим, у вас минимальная настройка: вы добавляете обработчик клика к фигуре Plotly, но не принимаете никаких аргументов в обработчике и вызываете notify без сообщения. Выглядит просто, но работать как задумано не будет.

import plotly.graph_objects as go
from nicegui import ui

def on_click_bad():
    ui.notify()

chart_fig = go.Figure(go.Scatter(x=[1, 2, 3, 4], y=[1, 2, 3, 2.5]))
chart_fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))
plot_area = ui.plotly(chart_fig).classes('w-full h-40')
plot_area.on('plotly_click', on_click_bad)

ui.run()

Что же идёт не так

Привязка события клика корректна, но цель мешают два нюанса. Во‑первых, обработчик взаимодействия с Plotly получает один аргумент — объект события — и его нужно принять, чтобы получить доступ к данным. Во‑вторых, вызов notify без сообщения некорректен: туда следует передавать строку. Есть и поведенческая тонкость: Plotly генерирует событие только при клике по отрисованной точке или элементу графика, а не по пустому месту.

Событие, приходящее на plotly_click, — это экземпляр GenericEventArguments. Его полезная нагрузка доступна через словарь e.args, где и лежат нужные детали. Индекс кликнутой точки находится по пути e.args['points'][0]['pointIndex'].

Решение: извлекаем индекс из события

Определите обработчик так, чтобы он принимал событие, и прочитайте индекс из его args. Если хотите сохранить значение для последующего использования, положите его в переменную на уровне модуля — это обычная практика в GUI — либо обрабатывайте его сразу внутри обработчика.

import plotly.graph_objects as go
from nicegui import ui

selected_point_idx = None

def on_point_click(evt):
    global selected_point_idx
    selected_point_idx = evt.args['points'][0]['pointIndex']
    ui.notify(f'Clicked index: {selected_point_idx}')

plot_obj = go.Figure(go.Scatter(x=[1, 2, 3, 4], y=[1, 2, 3, 2.5]))
plot_obj.update_layout(margin=dict(l=0, r=0, t=0, b=0))
ui.plotly(plot_obj).classes('w-full h-40').on('plotly_click', on_point_click)

ui.run()

Такой обработчик считывает индекс точки напрямую из полезной нагрузки события и присваивает его Python‑переменной. Вызов notify демонстрирует, что всё связано правильно, и соблюдает требование вызывать notify со строкой.

Как быстро посмотреть, что именно отправляет событие

При первом подключении обработчика удобно вывести всё, что приходит в событии, чтобы подтвердить структуру. Это быстрый способ увидеть доступные ключи в e.args и убедиться, что событие действительно срабатывает.

def inspect_event(*args, **kwargs):
    print(args, kwargs)

# подключим временно
# ui.plotly(plot_obj).on('plotly_click', inspect_event)

После этого вы увидите, какие данные приходят, и сможете сузить их до нужных полей.

Почему это важно

Обработчики событий — связующее звено между фронтендом Plotly и состоянием на стороне Python в NiceGUI. Если сигнатура обработчика неверна или вы игнорируете полезную нагрузку, теряется возможность осмысленно реагировать на действия пользователя. Понимание того, что plotly_click отдаёт GenericEventArguments и что данные лежат в e.args, позволяет надёжно строить интерактивное поведение — от получения индекса кликнутой точки до запуска последующих обновлений интерфейса.

Главное

Всегда принимайте объект события в обработчике, чтобы читать e.args. Для индекса кликнутой точки используйте e.args['points'][0]['pointIndex']. Если нужно визуальное подтверждение, вызывайте ui.notify со строкой. Помните, что plotly_click возникает при клике по элементу графика. А если значение понадобится позже, сохраните его в переменной, живущей дольше обработчика.

Статья основана на вопросе на StackOverflow от DrewGIS и ответе Falko.