How to store click position without global variables? Matplotlib/Python - python

I'm trying to stop a random plotting with just clicking the screen, but the examples I've seen use global variables and I don't want to use them. So what can I do?
This is my code so far. The function onclick is an idea I took from Matplotlib documentation, but they used it to print information on the console and I want to store it in a variable and then return it (for example in one variable called "get_mouse" and then break the while-cicle).
Pd: I know there're some crimes, sorry I'm a beginner...
import matplotlib.pyplot as plt
import numpy as np
def onclick(event):
if event.dblclick :
print('help me')
plt.ion()
B=np.random.random((1,5))
fig = plt.figure()
ax = plt.axes(xlim=(-2,2),ylim=(-2,2))
line, = ax.plot([],[],'bo', ms=4)
cid = fig.canvas.mpl_connect('button_press_event', onclick)
while True:
S=np.random.random((1,5))
line.set_data(B+S,B-S)
fig.canvas.draw()
fig.canvas.flush_events()
plt.pause(0.5)
if get_mouse == True:
break

Related

Dynamically updating Matplotlib

I want to update a single Matplotlib figure with each timestep in my Notebook. I have referenced numerous API resources and examples forum answers here on StackOverflow, however, all of the proposed code either did not graph or displayed a new figure with each timestep, like this answer,
import matplotlib.pyplot as plt
import time
import random
from collections import deque
import numpy as np
# simulates input from serial port
def random_gen():
while True:
val = random.randint(1,10)
yield val
time.sleep(0.1)
a1 = deque([0]*100)
ax = plt.axes(xlim=(0, 20), ylim=(0, 10))
d = random_gen()
line, = plt.plot(a1)
plt.ion()
plt.ylim([0,10])
plt.show()
for i in range(0,20):
a1.appendleft(next(d))
datatoplot = a1.pop()
line.set_ydata(a1)
plt.draw()
print a1[0]
i += 1
time.sleep(0.1)
plt.pause(0.0001) #add this it will be OK.
and this answer.
import numpy as np
import matplotlib.pyplot as plt
plt.axis([0, 10, 0, 1])
for i in range(10):
y = np.random.random()
plt.scatter(i, y)
plt.pause(0.1)
How can I update a figure with each timestep in Python, via Matplotlib or possibly other means? I appreciate your perspectives.
Thank you :)
Real-time drawing by entering the interactive mode of matplotlib.
If you only use plt.show() to draw, the program will stop executing the subsequent program, so open the drawing window through plt.ion() to enter the interactive mode, use the program plt.plot() to draw in real time, after the drawing is completed, use plt .ioff() exits the interactive mode and uses plt.show() to display the final image data. If plt.show() is not added at the end, it will flash back.
import matplotlib.pyplot as plt
import numpy as np
ax=[]
ay=[]
bx=[]
by=[]
num=0
plt.ion()
# plt.rcParams['savefig.dpi'] = 200
# plt.rcParams['figure.dpi'] = 200
plt.rcParams['figure.figsize'] = (10, 10)
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['lines.linewidth'] = 0.5
while num<100:
plt.clf()
plt.suptitle("TITLE",fontsize=30)
g1=np.random.random()
ax.append(num)
ay.append(g1)
agraphic=plt.subplot(2,1,1)
agraphic.set_title('TABLE1')
agraphic.set_xlabel('x',fontsize=10)
agraphic.set_ylabel('y', fontsize=20)
plt.plot(ax,ay,'g-')
#table2
bx.append(num)
by.append(g1)
bgraghic=plt.subplot(2, 1, 2)
bgraghic.set_title('TABLE2')
bgraghic.plot(bx,by,'r^')
plt.pause(0.4)
if num == 15:
plt.savefig('picture.png', dpi=300)
#break
num=num+1
plt.ioff()
plt.show()

Why do you have to instantiate the matplotlib.animation.FuncAnimation for it to work? [duplicate]

If I create a file test.py with the code
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
if __name__ == '__main__':
fig = plt.figure()
title = fig.suptitle("Test _")
def anim(i):
title.set_text("Test %d" % i)
plt.plot([0,1], [0,1])
FuncAnimation(fig, anim)
plt.show()
and try to run it in my command line, using python test.py, I get an empty screen with the title Test _ and without any axes.
The same is true when running with python -i test.py, but if I now enter the same code in the interactive session
>>> fig = plt.figure()
>>> title = fig.suptitle("Test _")
>>> FuncAnimation(fig, anim)
>>> plt.show()
everything just works as expected.
I have been looking at this for so long now and I don't seem to find any issues or questions that are related to this. I am using matplotlib 2.0.0 in python 3.5.2 on OS X.
Is this a (known) bug? Anyone with ideas why this might be happening or how this could be resolved?
From the animation documentation: "[..] it is critical to keep a reference to the instance object."
So you need to keep the FuncAnimation instance alive by assigning it to a variable.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
if __name__ == '__main__':
fig = plt.figure()
title = fig.suptitle("Test _")
def anim(i):
title.set_text("Test %d" % i)
plt.plot([0,1], [0,1])
ani = FuncAnimation(fig, anim)
plt.show()
There is an ongoing discussion about whether the Animation should be stored internally or not.

matplotlib.close() does not close plot

I'm using Python 3.6 in jupyter notebook. plt.close does not close plot. I tried with plt.ion() also and many other ways.
I want to display image, then wait for pause or input() and then remove the previous image and show the new one.
import matplotlib.pyplot as plt
from time import sleep
from scipy import eye
plt.imshow(eye(3))
plt.show()
sleep(1)
plt.close()
Here is an example that shows a sequence of plots, each for one second. Essential are the commants plt.show(block = False) and plt.pause(1) instead of sleep(1):
import numpy as np
import matplotlib.pyplot as plt
def show_image(n):
fig, ax = plt.subplots()
x = np.linspace(0,1,100)
y = x**n
ax.plot(x,y, label = 'x**{}'.format(n))
ax.legend()
plt.show(block=False)
plt.pause(1)
plt.close(fig)
for i in range(10):
show_image(i)
If I understand correctly, what you want is to show a plot, wait 1 second, then let it close automatically.
This would be achieved as follows.
import matplotlib.pyplot as plt
from scipy import eye
plt.imshow(eye(3))
def show_and_close(sec):
timer = plt.gcf().canvas.new_timer(interval=sec*1000)
timer.add_callback(lambda : plt.close())
timer.single_shot = True
timer.start()
plt.show()
show_and_close(1)

multiple interactive matplotlib behave differently in python 2.7 vs 3.6

I have a python script that loops continuously with user input directing it to get data from a file then plots it using matplotlib. My hope is to have the plots show up and stick around (and continue to be interactive) while the script continues on in the console asking for the next round of user input and plotting. So the user might end up with a few plot windows open in the end that are all functional.
I actually have it working in my python 2.7 anaconda build with the following example code that just plots a random dataset each loop.
import sys
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
print('matplotlib version: '+matplotlib.__version__)
version = sys.version_info[0]
def plot_data(data):
fig = plt.figure(figsize=(6,6))
plt.plot(data)
plt.ion()
plt.show()
plt.pause(0.001)
while True:
if version < 3:
option = raw_input('type (1) to plot next data, (q) to quit\n')
else:
option = input('type (1) to plot next data, (q) to quit\n')
if option == '1':
data = np.random.choice(1000,100, replace = False)
plot_data(data)
elif option == 'q':
break
Running the code with my python 3.6 build, the plot shows up but is frozen in a non-responding state until I go back to the console and type in q to quit my user input loop. In python 2.7 the plot windows are fully functional through multiple input loops and plot calls. So I'm hoping someone knows what difference here. I'm printing the matplotlib version and it seems like both my 2.7 and 3.6 environments are using the same matplotlib 2.0.2 so maybe it's in the GUI handler?
I'm not sure if the setup from the question using a while loop and plt.ion() is actually supposed to work. At least for me using python 2.7 and matplotlib 2.1 it doesn't.
I would have though that this is to be expected, because the application freezes and becomes unresponsive as long as it waits for user input.
In any case, since it is always a bit delicate to mix the console and a GUI application, I would choose to work completely inside the event loop of the GUI.
You may connect "key_press_event"s to the "1" and "q" key and show new data inside the open matplotlib window.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(6,6))
ax = plt.subplot(111)
def plot_data(ax, data):
ax.clear()
ax.plot(data)
ax.figure.canvas.draw_idle()
def press(event=None):
if event:
if event.key == "1":
data = np.random.choice(1000,100, replace = False)
plot_data(ax, data)
elif event.key == "q":
plt.close()
cid= fig.canvas.mpl_connect("key_press_event", press)
plt.show()
In case you want to create several figures, this would look like
import matplotlib.pyplot as plt
import numpy as np
def create_figure(data):
fig, ax = plt.subplots(figsize=(6,6))
ax.plot(data)
cid = fig.canvas.mpl_connect("key_press_event", press)
fig.show()
def plot_data():
data = np.random.choice(1000,100, replace = False)
create_figure(data)
def press(event=None):
if event:
if event.key == "1":
plot_data()
elif event.key == "q":
plt.close("all")
else:
plot_data()
press()
plt.show()
For more complicated input, you may create a small GUI, which collects the input and calls the function for plotting. As an example, one may use Tkinter for that purpose.
from Tkinter import * # use from tkinter import * if using python3
import matplotlib.pyplot as plt
import numpy as np
def create_figure(data):
fig, ax = plt.subplots(figsize=(6,6))
ax.plot(data)
fig.show()
def plot_data(n, amp):
try:
n = int(n)
amp = float(amp)
data = amp*np.random.rand(n)
create_figure(data)
except:
pass
def closeall():
for i in plt.get_fignums():
plt.close(i)
def create_input():
root = Tk()
Label(text="Number of datapoints").grid(row=1,column=0, sticky=W)
Label(text="Amplitude").grid(row=2,column=0, sticky=W)
tb1=Entry(root,textvariable=StringVar(root, value='24'))
tb1.grid(row=1,column=1, sticky=W+E)
tb2=Entry(root,textvariable=StringVar(root, value='6'))
tb2.grid(row=2,column=1, sticky=W+E)
b1=Button(root,text=" Plot ",command= lambda : plot_data(tb1.get(),tb2.get()))
b1.grid(row=4,column=1, sticky=W+E)
b2=Button(root,text="Close All",command=closeall)
b2.grid(row=5,column=1, sticky=W+E)
root.mainloop()
create_input()

Pylab only updating after raw_input call

I'm trying to continuously read from a file and plot using matplotlib as a way to create a bit of an animation. Here is the working code:
import numpy as np
import pylab
import time
print "start"
pylab.ion() # Unfortunately, will still need to run pylab.show() all the time
fig = pylab.figure()
ax = fig.add_subplot(1,1,1)
ax.set_xlim((0,128))
ax.set_ylim((0,128))
#Initialize the circle object to a silly location
f1 = pylab.Circle((60,68), radius=2, fc='y')
ax.add_patch(f1)
pylab.show()
# Start the animation
for i in range(0,1000):
if(i%2 == 0):
f1.center = 30, 30
else:
f1.center = 40, 40
pylab.show()
raw_input("Press Enter to continue...")
print("Updating")
#time.sleep(2) # This is going to be used later
Now, I want to replace the user input with a sleep timer:
import numpy as np
import pylab
import time
print "start"
pylab.ion() # Unfortunately, will still need to run pylab.show() all the time
fig = pylab.figure()
ax = fig.add_subplot(1,1,1)
ax.set_xlim((0,128))
ax.set_ylim((0,128))
#Initialize the circle object to a silly location
f1 = pylab.Circle((60,68), radius=2, fc='y')
ax.add_patch(f1)
pylab.show()
# Start the animation
for i in range(0,1000):
if(i%2 == 0):
f1.center = 30, 30
else:
f1.center = 40, 40
pylab.show()
#raw_input("Press Enter to continue...")
print("Updating")
time.sleep(2) # This is going to be used later
But this code doesn't work! It sleeps, but nothing is updated. What am I doing wrong?
You need to replace time.sleep(2) with pylab.pause(2) as indicated in the comments of this question.

Categories