Закрыть интерактивное окно matplotlib в jupyter по кнопке прерывания
Столкнулся с такой проблемой:
Необходимо написать код в jupyter notebook, который бы в реальном времени выводил график по данным, считываемым с внешних источников (условно осциллограф). Бэкенд matplotlib - qt5.
С самой программой проблем нет, однако выявилась следующая неприятная особенность библиотеки matplotlib в юпитере:
Если прервать весь процесс в ноутбуке - окно графика не закрывается

При том, если тот же код перенести в обычный .py файл - все работает отлично и при прерывании процесса все окна закрываются
Для примера приведу простую конструкцию:
%matplotlib qt5
import matplotlib.pyplot as plt
fig = plt.figure()
fig.show()
При выполнении в юпитере открывается всплывающее окно с пустым графиком, но если остановить процесс - оно не закрывается.
Хотел бы поинтересоваться, как сделать так, чтобы при прерывании в юпитере все окна закрывались?
Ответы (1 шт):
Так и должно быть.
Кнопка "Квадрат" не останавливает процесс, а посылает в процесс сообщение "прервать выполнение текущей ячейки". В вашем случае после plt.show() ячейка считается выполненой, поэтому "Квадрат" игнорируется.
Чтобы закрыть все открытые окна, нужно выполнить в новой ячейке код plt.close('all').
Если же вы хотите имитировать блокирующий вызов графического окна, чтобы ячейка была активной до его закрытия, то можно сделать вот так:
fig = plt.figure()
plt.show()
try:
# Пока есть активные рисунки
while len(plt.get_fignums()) > 0:
# передавать управление в графический event loop
# Если это не делать, ничего не будет нарисовано
plt.pause(0.1)
except:
# При нажатии на черный квадрат в python event loop вбрасывается KeyboardInterrupt
# закрыть все активные окна
plt.close('all')
Как это работает.
Чтобы кнопка "Квадрат" сработала, нужно, чтобы ячейка была активной. Для этого нужно в конце ячейки добавить (бесконечный) цикл. По нажатию кнопки "Квадрат" в пайтоновский event loop будет вброшено прерывание KeyboardInterrupt. Нужно поймать это прерывание и закрыть все текущие окна.
Окошко рисуется в другом event loop, в котором KeyboardInterrupt не видно. Поэтому нужно внутри цикла передавать управление в графический event loop только на время. Это делается функцией pyplot.pause.
Совсем бесконечный цикл не заметит закрытия окна. Поэтому нужно добавить условие завершения цикла когда все окна закрыты. Функция pyplot.get_fignums возвращает список номеров активных окон (figures). Когда пользователь закроет все окна, эта функция вернёт пустой список. Тогда можно завершить цикл и выполнение ячейки.