Reputation: 159
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?
Upvotes: 3
Views: 408
Reputation: 339430
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()
Upvotes: 1