Построение без блокировки с помощью Matplotlib

Я играл с Numpy и matplotlib за последние несколько дней. У меня возникают проблемы с попыткой сделать график matplotlib функцией без блокировки выполнения. Я знаю, что на SO уже есть много тем, которые задают похожие вопросы, и я довольно много искал в googled, но не смог выполнить эту работу.

Я попытался использовать show (block = False), как предлагают некоторые люди, но все, что я получаю, - это замороженное окно. Если я просто вызываю show(), результат отображается правильно, но выполнение блокируется до закрытия окна. Из других потоков, которые я прочитал, я подозреваю, что работает или нет (block = False), зависит от бэкэнда. Это верно? Мой конец - Qt4Agg. Не могли бы вы взглянуть на мой код и сказать мне, если вы видите что-то не так? Вот мой код. Спасибо за любую помощь.

from math import *
from matplotlib import pyplot as plt
print plt.get_backend()



def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print y

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __name__ == '__main__':
    main()

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

Ответ 1

Я долго искал решения и нашел этот ответ.

Похоже, для того, чтобы получить то, что вы (и я) хотите, вам нужна комбинация plt.ion(), plt.show() (не с blocking=False, которая устарела) и, самое главное, plt.pause(.001) ( или какое бы то ни было время). pause необходим, потому что события GUI происходят во время сна, включая основной рисунок. Возможно, что это реализовано за счет сбора времени со спящего потока, поэтому, возможно, IDE с этим связаны - я не знаю.

Вот реализация, которая работает для меня на python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __name__ == '__main__':
    main()

Ответ 2


Простой трюк, который работает для меня, заключается в следующем:

  1. Используйте аргумент block = False внутри show: plt.show(block = False)
  2. Используйте другой plt.show() в конце скрипта .py.

Пример:

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

Примечание: plt.show() - последняя строка моего скрипта.

Ответ 3

Вы можете избежать блокирования выполнения, записав график в массив, а затем отобразив массив в другом потоке. Вот пример генерации и отображения графиков одновременно с использованием pf.screen из pyformulas 0.2.8:

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

Результат:

Sine animation

Отказ от ответственности: я сопровождаю пиформулы.

Ссылка: Matplotlib: сохранить график в массив NumPy

Ответ 4

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

Вы можете использовать plt.ion() если хотите, но я обнаружил, что использование plt.draw() столь же эффективно

Для моего конкретного проекта я рисую изображения, но вы можете использовать plot() или scatter() или что-то еще вместо figimage(), это не имеет значения.

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

Или же

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

Если вы используете фактическую цифру.
Я использовал ответы @krs013 и @Default Picture, чтобы понять это
Надеюсь, это спасет кого-то от необходимости запускать каждую фигуру в отдельном потоке или от необходимости читать эти романы, просто чтобы понять это.

Ответ 5

Живое изображение

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

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

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

Участок после выхода из программы

Это была моя настоящая проблема, на которую не удалось найти удовлетворительного ответа, я хотел построить график, который не закрывался после завершения сценария (например, MATLAB),

Если вы думаете об этом, после того, как скрипт завершен, программа завершается, и нет логического способа сохранить график таким образом, поэтому есть два варианта

  1. заблокировать выход из скрипта (это plt.show(), а не то, что я хочу)
  2. запустить сюжет в отдельном потоке (слишком сложно)

это не было удовлетворительным для меня, поэтому я нашел другое решение за пределами коробки

SaveToFile и просмотр во внешней программе просмотра

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

Выбор формата для сохранения

векторные форматы являются небольшими и быстрыми

  • SVG хорош, но не может найти хорошего зрителя, кроме веб-браузера, который по умолчанию требует обновления вручную
  • PDF может поддерживать векторные форматы, и есть облегченные средства просмотра, которые поддерживают живое обновление

Быстрый легкий просмотрщик с обновлением в реальном времени

Для PDF есть несколько хороших вариантов

  • В Windows я использую SumatraPDF, которая бесплатна, быстра и легка (для моего случая используется только 1,8 МБ ОЗУ)

  • В Linux есть несколько опций, таких как Evince (GNOME) и Ocular (KDE)

Пример кода и результаты

Пример кода для вывода графика в файл

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

после первого запуска откройте выходной файл в одном из указанных выше средств просмотра и наслаждайтесь.

Вот снимок экрана VSCode вместе с SumatraPDF, также процесс достаточно быстрый, чтобы получить частоту time.sleep() я могу получить около 10 Гц на моей установке, просто используя time.sleep() между интервалами) pyPlot,Non-Blocking

Ответ 6

Drawnow пакета Python позволяет обновлять график в реальном времени неблокирующим образом.
Он также работает с веб-камерой и OpenCV, например, для построения мер для каждого кадра.
Смотрите оригинальный пост.

Ответ 7

Мне было проще всего найти ответ "Игги", но я получил следующую ошибку при выполнении последующей команды subplot, которой не было, когда я просто делал show:

MatplotlibDeprecationWarning: добавление осей с использованием тех же аргументов, что и у предыдущих осей, в настоящее время повторно использует более ранний экземпляр. В будущей версии новый экземпляр всегда будет создаваться и возвращаться. Между тем, это предупреждение можно подавить и обеспечить дальнейшее поведение, передав уникальную метку каждому экземпляру осей.

Чтобы избежать этой ошибки, это помогает закрыть (или очистить) график после того, как пользователь нажимает ввод.

Вот код, который работал для меня:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()