MatPlotLib with ion() does not show window - python

If I run the following code:
import matplotlib.pyplot as plt
import numpy as np
#plt.ion()
while True:
print('loop')
x = range(10)
y = np.random.rand(10)
plt.scatter(x, y)
plt.show()
Then I see a scatter plot displayed on my screen. Then each time I close the window for the plot, it displays a new plot with new data.
However, if I uncomment the line plt.ion(), nothing is displayed at all. There is no window created, and the program just continues through the loop, printing out 'loop'.
I want to be able to display a graph, and then return to the code automatically, with the graph still displayed. How can I do this?

If you want to plot on top of the same figure window, rather than generating a new window at every iteration the following will work:
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig, ax = plt.subplots(1, 1)
while True:
# If wanting to see an "animation" of points added, add a pause to allow the plotting to take place
plt.pause(1)
x = range(10)
y = np.random.rand(10)
ax.scatter(x, y)
The result you see will depend on the which matplotlib backend you are using. If you're wanting to see the new points being added you should use Qt4 or Qt5

Related

Adding a second plot to an existing matplotlib chart

I would like to have a function that adds plots to an existing chart when called. Right now my empty plot shows, but called the function never seems to happen as it waits until I close the chart window. The program then ends without reopening the chart window.
import numpy as np
import matplotlib.pyplot as plt
import time
fig, ax = plt.subplots()
plt.show()
def plotting(slope, intercept):
x_vals = np.array(ax.get_xlim())
y_vals = intercept + slope * x_vals
ax.plot(x_vals, y_vals, '-')
plt.show()
plotting(10,39)
time.sleep(1)
plotting(5,39)
plt.show() is meant to be called once at the end of the script. It will block until the plotting window is closed.
You may use interactive mode (plt.ion()) and draw the plot at intermediate steps (plt.draw()). To obtain a pause, don't use time.sleep() because it'll let the application sleep literally (possibly leading to freezed windows). Instead, use plt.pause(). At the end, you may turn interactive mode off again (plt.ioff()) and finally call plt.show() in order to let the plot stay open.
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
fig, ax = plt.subplots()
def plotting(slope, intercept):
x_vals = np.array(ax.get_xlim())
y_vals = intercept + slope * x_vals
ax.plot(x_vals, y_vals, '-')
plt.draw()
plotting(10,39)
plt.pause(1)
plotting(5,39)
plt.ioff()
plt.show()
Send the optional keyword argument block=False to plt.show().
Explanation: the plot window blocks the program from continuing. Sending this argument will allow the program to continue.
Notice that if you only use that argument and the program ends, then the plot window is closed. Therefore you might want to call plt.show(block=True) or plt.waitforbuttonpress() at the end of the program.
Personally I would go for adding a block argument for your own function:
def plotting(slope, intercept, block=True):
x_vals = np.array(ax.get_xlim())
y_vals = intercept + slope * x_vals
ax.plot(x_vals, y_vals, '-')
plt.show(block=block)
plotting(10,39,False)
time.sleep(1)
plotting(5,39)

Updating pyplot graph in real time

I'm trying to plot a 2D grid of data and map them to colors. Then I want to update the values and have the graph update with the new values. Currently the graph only shows the final result, not all the middle phases the graph should go through.
MY CODE::
import matplotlib.pyplot as pyplot
import matplotlib as mpl
import numpy as np
import time
import matplotlib.animation as animation
thing=0
NUM_COL=10
NUM_ROW=10
zvals=np.full((NUM_ROW,NUM_COL),-5.0)
def update_graph(zvals):
zvals+=1
pyplot.clf()
img = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap,norm=norm)
time.sleep(1)
pyplot.draw()
# make a color map of fixed colors
cmap = mpl.colors.ListedColormap(['blue','black','red'])
bounds=[-6,-2,2,6]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
# tell imshow about color map so that only set colors are used
img = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap,norm=norm)
# make a color bar
pyplot.colorbar(img,cmap=cmap,norm=norm,boundaries=bounds,ticks=[-5,0,5])
pyplot.draw()
for i in range(5):
update_graph(zvals)
pyplot.show()
pyplot does not generally show anything until pyplot.show() is called, unless matplotlib runs in 'interactive' mode. The interactive mode is entered by calling pyplot.ion() and can exited again by calling pyplot.ioff().
Thus it should be possible for you to see all your updates by calling pyplot.ion() somewhere before doing anything you want to be directly updated and then end your program with pyplot.ioff() to get back to the standard pyplot way when done.
However, it may not look very smooth, depending on your system and what updates you are doing.
So I'm not sure if this a good answer or not, I have only used updating plots once before. But this is a way to achieve what you want.
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
NUM_COL = 10
NUM_ROW = 10
zvals = np.full((NUM_ROW,NUM_COL),-5.0)
cmap = mpl.colors.ListedColormap(['blue','black','red'])
bounds = [-6,-2,2,6]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig = plt.figure() # Create the figure
img = plt.imshow(zvals,interpolation='nearest', cmap=cmap,norm=norm) # display the first image
plt.colorbar(img,cmap=cmap,norm=norm,boundaries=bounds,ticks=[-5,0,5]) # create your colour bar
# If we dont have this, then animation.FuncAnimation will call update_graph upon initialization
def init():
pass
# animation.FuncAnimation will use this function to update the plot. This is where we update what we want displayed
def update_graph(frame):
global zvals # zvals is a global variable
zvals+=1
img.set_data(zvals) # This sets the data to the new, updated values
print("Frame Update {}".format(frame)) # this is for debugging to help you see whats going on
return img
# This is what will run the animations
anim = animation.FuncAnimation(fig, update_graph, init_func = init,
interval = 1000, # update every 1000ms
frames = 8, # Update 8 times
repeat=False) # After 8 times, don't repeat the animation
plt.show() # show our plot

How to keep matplotlib (python) window in background?

I have a python / matplotlib application that frequently updates a plot with new data coming in from a measurement instrument. The plot window should not change from background to foreground (or vice versa) with respect to other windows on my desktop when the plot is updated with new data.
This worked as desired with Python 3 on a machine running Ubuntu 16.10 with matplotlib 1.5.2rc. However, on a different machine with Ubuntu 17.04 and matplotlib 2.0.0, the figure window pops to the front every time the plot is updated with new data.
How can I control the window foreground/background behavior and keep the window focus when updating the plot with new data?
Here's a code example illustrating my plotting routine:
import matplotlib
import matplotlib.pyplot as plt
from time import time
from random import random
print ( matplotlib.__version__ )
# set up the figure
fig = plt.figure()
plt.xlabel('Time')
plt.ylabel('Value')
plt.ion()
# plot things while new data is generated:
t0 = time()
t = []
y = []
while True:
t.append( time()-t0 )
y.append( random() )
fig.clear()
plt.plot( t , y )
plt.pause(1)
matplotlib was changed somewhere from version 1.5.2rc to 2.0.0 such that pyplot.show() brings the window to the foreground (see here). The key is therefore to avoid calling pyplot.show() in the loop. The same goes for pyplot.pause().
Below is a working example. This will still bring the window to the foreground at the beginning. But the user may move the window to the background, and the window will stay there when the figure is updated with new data.
Note that the matplotlib animation module might be a good choice to produce the plot shown in this example. However, I couldn't make the animation work with interactive plot, so it blocks further execution of other code. That's why I could not use the animation module in my real-life application.
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import time
from random import random
print ( matplotlib.__version__ )
# set up the figure
plt.ion()
fig = plt.figure()
ax = plt.subplot(1,1,1)
ax.set_xlabel('Time')
ax.set_ylabel('Value')
t = []
y = []
ax.plot( t , y , 'ko-' , markersize = 10 ) # add an empty line to the plot
fig.show() # show the window (figure will be in foreground, but the user may move it to background)
# plot things while new data is generated:
# (avoid calling plt.show() and plt.pause() to prevent window popping to foreground)
t0 = time.time()
while True:
t.append( time.time()-t0 ) # add new x data value
y.append( random() ) # add new y data value
ax.lines[0].set_data( t,y ) # set plot data
ax.relim() # recompute the data limits
ax.autoscale_view() # automatic axis scaling
fig.canvas.flush_events() # update the plot and take care of window events (like resizing etc.)
time.sleep(1) # wait for next loop iteration
For the tkinter backend (matplotlib.use("TkAgg")), using flush_events is not sufficient: you also need to call fig.canvas.draw_idle() before each fig.canvas.flush_events(). As #samlaf wrote, the same holds for the Qt5Agg backend.

Matplotlib widget used on a plot produced by another matplotlib widget

I am trying to use matplotlib LassoSelector to select some points from a scatter plot and produce a separate figure for selected points only. When I try to use another matplotlib widget on the second plot it doesn't work but there is no error or warning message. Below is a minimal example with LassoSelector and SpanSelector used.
I tried other widgets too; the Button widget displays the button but the action on the button press is not performed.
import numpy as np
from matplotlib.pyplot import *
from matplotlib.widgets import SpanSelector, LassoSelector
from matplotlib.path import Path
def onselect(verts):
global xys,data
#get indexes of selected points
path = Path(verts)
xysn = xys.get_offsets()
ind = np.nonzero([path.contains_point(xy) for xy in xysn])[0]
#plot the second figure
fig=figure(2)
ax=fig.add_subplot(111)
ax.hist(data[:,0][ind],10)
#this should be executed when SpanSelector is used
def action(min,max):
print min,max
#try to do SpanSelector (this fails)
span=SpanSelector(ax,action,'horizontal')
show()
#initialize a figure
fig=figure(1)
ax=fig.add_subplot(111)
#create data
data=np.array([[1,6], [4,8],[0,4],[4,2],[9,6],[10,8],[2,2],[5,5],[0,4],[4,5]])
#plot data
xys=ax.scatter(data[:,0],data[:,1])
#select point by drawing a path around them
lasso = LassoSelector(ax, onselect=onselect)
show()
matplotlib widgets are event driven, so wait for user input. The problem with you code is you are trying to create a new figure with a new event handler SpanSelector. I'm not sure if you can add new events as a result of previous ones and with SpanSelector commented out, I get the following error,
QCoreApplication::exec: The event loop is already running
So the new event, LassoSelector is not registered and user input is not picked up (and the new figure doesn't appear). It is better to create all figures and register all possible events at the beginning of the code. The following should be closer to what you want to do,
import numpy as np
from matplotlib.pyplot import *
from matplotlib.widgets import SpanSelector, LassoSelector
from matplotlib.path import Path
#this should be executed when LassoSelector is used
def onselect(verts):
global xys,data
#get indexes of selected points
path = Path(verts)
xysn = xys.get_offsets()
ind = np.nonzero([path.contains_point(xy) for xy in xysn])[0]
#Clear and update bar chart
h, b = np.histogram(data[:,0][ind],10)
for rect, bars in zip(rects, h):
rect.set_height(bars)
ax2.bar(mb, h, align='center')
draw()
#this should be executed when SpanSelector is used
def action(min,max):
print min,max
#initialize figures
fig1=figure(1)
ax1=fig1.add_subplot(111)
fig2=figure(2)
ax2=fig2.add_subplot(111)
#create data
data=np.array([[1,6],[4,8],[0,4],[4,2],[9,6],[10,8],[2,2],[5,5],[0,4],[4,5]])
#plot data
xys=ax1.scatter(data[:,0],data[:,1])
#Plot initial histogram of all data
h, b = np.histogram(data[:,0],10)
mb = [0.5*(b[i]+b[i+1]) for i in range(b.shape[0]-1)]
rects = ax2.bar(mb, h, align='center')
#Register lasso selector
lasso = LassoSelector(ax1, onselect=onselect)
#Register SpanSelector
span=SpanSelector(ax2,action,'horizontal')
show()
Note, in order to update bar charts, it's a little more tricky than plots so I used this answer here Dynamically updating a bar plot in matplotlib
For some reason, the histogram figure 2 only updates when you click on it. I would consider using a single figure with two axes for this which may be easier to work with,
fig, ax = subplots(2,1)
ax1 = ax[0]; ax2 = ax[1]

"Reset original view" does not show the whole plot

I'm plotting a line and updating it in a loop. When I pan the plot at some point during the execution and then click "Reset original view" in the interactive matplotlib window, I am taken back to the plot state from the moment when I started zooming/panning it. Is there a way to see the full extents of the plot instead? Even better, is there a way to tell matplotlib to keep updating the view after this operation?
python 3.4.3, matplotlib 1.4.3
import matplotlib
matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
import numpy as np
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
values_v = []
values_i = []
ln1, = ax1.plot(values_i, values_v, color='green')
plt.ion()
plt.show()
for i in range(40):
scopevals = [i, i+2+np.random.rand()]
values_v.append(scopevals[0])
values_i.append(scopevals[1])
ln1.set_data([values_i, values_v])
ax1.relim()
ax1.autoscale_view(True,True,True)
plt.pause(1)
I encountered the problem when I displayed different images in the same figure.
Clearing the old figure helped me:
plt.figure(1) #the figure you re working with
plt.clf() #clear figure
plt.imshow(self.sample_image) # show new picture
And the Original view should work again.
Cheers
Andy

Categories

Resources