Plotten op een niet-blokkerende manier met Matplotlib

Ik heb de afgelopen dagen met Numpy en matplotlib gespeeld. Ik heb problemen met het maken van een functie van matplotlib-plot zonder de uitvoering te blokkeren. Ik weet dat er hier al veel threads zijn op SO die soortgelijke vragen stellen, en ik heb behoorlijk wat gegoogled, maar het is me niet gelukt om dit te laten werken.

Ik heb geprobeerd show(block=False) te gebruiken, zoals sommige mensen suggereren, maar ik krijg alleen een bevroren venster. Als ik gewoon show() aanroep, wordt het resultaat correct weergegeven, maar wordt de uitvoering geblokkeerd totdat het venster wordt gesloten. Uit andere threads die ik heb gelezen, vermoed ik dat of show(block=False) werkt of niet, afhangt van de backend. Is dit correct? Mijn back-end is Qt4Agg. Kun je mijn code bekijken en me vertellen of je iets verkeerds ziet? Hier is mijn code. Bedankt voor alle hulp.

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. Ik vergat te zeggen dat ik het bestaande venster zou willen bijwerken elke keer dat ik iets plot, in plaats van een nieuw venster te maken.


Antwoord 1, autoriteit 100%

Ik heb lang gezocht naar oplossingen en vond dit antwoord .

Het lijkt erop dat je de combinatie van plt.ion(), plt.show()( niet met block=False) en, belangrijker nog, plt.pause(.001)(of wanneer je maar wilt). De pauzeis nodig omdat de GUI-gebeurtenissen plaatsvinden terwijl de hoofdcode slaapt, inclusief tekening. Het is mogelijk dat dit wordt geïmplementeerd door tijd op te halen uit een slapende thread, dus misschien knoeien IDE’s daarmee – ik weet het niet.

Hier is een implementatie die voor mij werkt op 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()

Antwoord 2, autoriteit 17%


Een eenvoudige truc die voor mij werkt, is de volgende:

  1. Gebruik het argument block = Falsein show: plt.show(block = False)
  2. Gebruik een andere plt.show()aan het eindevan het .py-script.

Voorbeeld:

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()

Opmerking: plt.show()is de laatste regel van mijn script.


Antwoord 3, autoriteit 9%

U kunt voorkomen dat de uitvoering wordt geblokkeerd door de plot naar een array te schrijven en de array vervolgens in een andere thread weer te geven. Hier is een voorbeeld van het gelijktijdig genereren en weergeven van plots met pf.screen van 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()

Resultaat:

 SINE-animatie

Disclaimer: ik ben de beheerder voor pyformulas.

Referentie: matplotlib: Sla plot op naar numpy array


4, Autoriteit 4%

Veel van deze antwoorden zijn super opgeblazen en van wat ik kan vinden, het antwoord is niet zo moeilijk te begrijpen.

Je kunt plt.ion()gebruiken als je wilt, maar ik vond het gebruik van plt.draw()net zo effectief

Voor mijn specifieke project ben ik afbeeldingen aan het plotten, maar je kunt plot()of scatter()of wat dan ook gebruiken in plaats van figimage(), het maakt niet uit.

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

Of

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

Als je een echt cijfer gebruikt.
Ik heb @krs013 en de antwoorden van @Default Picture gebruikt om dit uit te zoeken
Hopelijk voorkomt dit dat iemand elk figuur in een aparte thread moet lanceren, of deze romans moet lezen om dit uit te zoeken


Antwoord 5, autoriteit 3%

Ik ben erachter gekomen dat het commando plt.pause(0.001)het enige is dat nodig is en niets anders.

plt.show() en plt.draw() zijn onnodig en/of blokkeren op de een of andere manier. Dus hier is een code die een figuur tekent en bijwerkt en doorgaat. In wezen lijkt plt.pause(0.001) het dichtst bij de drawow van matlab te staan.

Helaas zullen die plots niet interactief zijn (ze bevriezen), behalve dat je een input()-commando invoegt, maar dan stopt de code.

De documentatie van de plt.pause(interval)commando zegt:

Als er een actief cijfer is, wordt het bijgewerkt en weergegeven vóór de pauze……
Dit kan worden gebruikt voor ruwe animatie.

en dit is vrijwel precies wat we willen. Probeer deze code:

import numpy as np
from matplotlib import pyplot as plt
x = np.arange(0, 51)               # x coordinates  
for z in range(10, 50):
    y = np.power(x, z/10)          # y coordinates of plot for animation
    plt.cla()                      # delete previous plot
    plt.axis([-50, 50, 0, 10000])  # set axis limits, to avoid rescaling
    plt.plot(x, y)                 # generate new plot
    plt.pause(0.1)                 # pause 0.1 sec, to force a plot redraw

Antwoord 6

Iggy’s antwoordwas voor mij het gemakkelijkst te volgen, maar ik kreeg de volgende foutmelding bij het uitvoeren van een volgende subplotcommando dat er niet was toen ik net show:

. deed

MatplotlibDeprecationWarning: Een assen toevoegen met dezelfde argumenten
aangezien een vorige assen momenteel de eerdere instantie hergebruikt. In een toekomst
versie, wordt er altijd een nieuwe instantie gemaakt en geretourneerd.
Ondertussen kan deze waarschuwing worden onderdrukt, en het toekomstige gedrag
gegarandeerd door een uniek label door te geven aan elke asseninstantie.

Om deze fout te voorkomen, helpt het om te sluiten (of wis) de plot nadat de gebruiker op enter heeft gedrukt.

Hier is de code die voor mij werkte:

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()

Antwoord 7

Het Python-pakket drawow maakt het mogelijk om een ​​plot in realtime op een niet-blokkerende manier bij te werken.
Het werkt ook met bijvoorbeeld een webcam en OpenCV om maten voor elk frame te plotten.
Zie het originele bericht.

Other episodes