Заставить SpanSelector ждать определенного события нажатия клавиши c - PullRequest
0 голосов
/ 28 февраля 2020

У меня есть скрипт, который порождает график данных с помощью виджета SpanSelector. Виджет вызывает функцию закрытия select_window(vmin, vmax), которая использует ограничения окна для анализа выбранных данных. Функция анализа порождает другой график с некоторыми визуальными результатами.

По умолчанию SpanSelector выполняет select_window сразу после выбора. Поскольку расчеты немного тяжелые, я бы хотел, чтобы пользователь подтвердил выбранное окно нажатием клавиши. Первый вариант - использовать plt.waitforbuttonpress, но он реагирует на все ключевые события, включая события, используемые по умолчанию для pan / zoom / et c. в matplotlib.

Второй вариант - подключить key_press_event напрямую, но я не был уверен, куда подключить и отключить обработчик событий.

Рабочий пример с использованием waitforbuttonpress:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector

def analyse(data, window_min, window_max):
    sliced_data = data[window_min:window_max]
    print(sliced_data)
    fig, ax = plt.subplots()
    ax.plot(sliced_data)
    plt.pause(0.001)

def plot_data(data):
    fig, ax = plt.subplots()
    ax.plot(data)
    def select_window(vmin, vmax):
        if plt.waitforbuttonpress(60):
            window_min = int(np.floor(vmin))
            window_max = int(np.ceil(vmax))
            analyse(data, window_min, window_max)
    widget = SpanSelector(
        ax, select_window, 'horizontal', useblit=True, span_stays=True,
        minspan=1
    )
    plt.show()
    return widget  # Keeping a reference so it isn't garbage collected.

if __name__ == '__main__':
    data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    widget = plot_data(data)

1 Ответ

0 голосов
/ 29 февраля 2020

Ключ должен был использовать fig.canvas.mpl_connect и fig.canvas.mpl_disconnect в правильных местах. Отключение необходимо, в противном случае графики будут накапливаться при последовательном выборе окна.

Это решение, которое принимает только ключ ввода в качестве действительного подтверждения окна. Другие ключи доступны через event.key.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector

def analyse(data, window_min, window_max):
    sliced_data = data[window_min:window_max]
    print(sliced_data)
    fig, ax = plt.subplots()
    ax.plot(sliced_data)
    plt.pause(0.001)

def plot_data(data):
    fig, ax = plt.subplots()
    ax.plot(data)
    def select_window(vmin, vmax):
        def _confirm_selection(event):
            if event.key == 'enter':  # Make the selector wait for <enter> key
                window_min = int(np.floor(vmin))
                window_max = int(np.ceil(vmax))
                analyse(data, window_min, window_max)
                fig.canvas.mpl_disconnect(cid)  # Disconnect the event after analysis
        cid = fig.canvas.mpl_connect('key_press_event', _confirm_selection)
    widget = SpanSelector(
        ax, select_window, 'horizontal', useblit=True, span_stays=True,
        minspan=1
    )
    plt.show()
    return widget  # Keeping a reference so it isn't garbage collected.

if __name__ == '__main__':
    data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    widget = plot_data(data)

Идентификатор события доступен для mpl_disconnect из замыкания. Таким образом, событие может быть отключено в пределах его собственного обратного вызова (то есть _confirm_selection). Я не уверен, что это лучший способ, и улучшения приветствуются, но это работает;)

...