I'm currently learning the matplotlib library on python 3.4. I'm practicing on a code where the user is supposed to interact with the plot only by clicking on it. When the click happens somewhere on the plot two things are supposed to happen: print out the y value and create a horizontal line in the plot where the click happened.
What I'm getting when I click somewhere on the plot is that the print happens as desired but the horizontal line shows up only if I press F (which fulls screen the plot). In other words, the line is created when I click on the plot but it doesn't appear untill I press F. Any ideas why it's happening?
The code:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(np.random.rand(10))
def pick(event):
plt.hlines(event.ydata,event.xdata-
0.2,event.xdata+0.2,colors='r',linestyle='solid')
print('Y coord = %f'%event.ydata)
fig.canvas.mpl_connect('button_press_event',pick)
plt.show()
You need to use interactive mode to update the plot after each click. Use plt.ion() before plt.show().
Related
Coming from a R background I am now moving on to Python and would like to learn Spyder 4 as a next tool alongside RStudio for data analysis. I am running into a problem however trying to learn matplotlib.
Having this code:
import matplotlib.pyplot as plt
x = [1,2,3,4,5,6,7,8,5,4,3,3,2,4,5,6]
y = [5,5,6,7,8,3,4,5,6,7,8,6,5,4,3,2]
plt.xlabel('x-as')
plt.ylabel('y-as')
plt.title('My title')
plt.legend()
plt.scatter(x, y, label = 'test')
plt.show()
This will not wait for the call to plt.show(), but instead plot a graph for each of the calls to a matplotlib functions, in the plot viewer section of the IDE.
How would I make Spyder 4 wait untill I actually want it to draw the graph?
You want to put fig = plt.figure() before the rest of the plotting lines. Also, you want to put plt.legend() after the scatter function so that the label shows.
I'm trying to control the display of a scatter plot with a checkbox. When I built it using the interact function it worked as expected. The plot was shown or hidden based on the value in the checkbox.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
def on_change(Display):
if Display == True:
plt.scatter(x,y)
plt.show()
return Display
interact(on_change, Display=False);
When I tried to do the same thing using the observe function every time I clicked on the checkbox I get an additional plot displayed below. What do I need to do to get it to redraw the same plot so it works like the example above?
I suppose something in the interact example is clearing the display but it's not clear how to do this manually.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
scat = plt.scatter(x,y)
plt.show()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
A couple of alterations I made to your example to hopefully demonstrate what you want. I have taken a more object-oriented route, not sure if you specifically wanted to avoid it but it helps achieve your desired outcome, it seems like you are moving towards a simple GUI here.
1) Include an Output widget (out) - basically a cell output which you can display like a normal widget. You can use a context manager block (with out:) when you want to print to that specific output widget. You can also clear the widget with out.clear_output()
2) Use the object oriented interface in matplotlib rather than using plt. I find this easier to control which plots are displayed and in which location at the right times.
temporarily suspend the interactive matplotlib with plt.ioff()
Create your figure and axis with fig, ax = plt.subplots(). NB figures can have multiple axes/subplots but we only need one.
'plot' the scatter data to your axis using ax.scatter(x,y), but this won't cause it to appear.
Explicitly display the figure with display(fig).
I'm assuming you want your figure to be replotted each time you check the box, so I have included it in the observe function. If your figure doesn't change, it would make sense to move it outside of the loop.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
out = widgets.Output()
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
with out:
plt.ioff()
fig,ax = plt.subplots()
ax.scatter(x,y)
display(fig)
else:
out.clear_output()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
display(out)
I have plotted an interactive figure, run the cell, and now all my keyboard presses are being captured by the interactive plot. How do I exist this without the mouse?
Shift-enter sort of works, but it seems to require there be a cell below the plot.
I think matplotlib recommends ctrl-w but as I am in a web browser (Jupyter) that would just close my tab.
The plot is within the cell.
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
fig,ax = plt.subplots(1,1)
x = np.linspace(0, 1, 100)
y = np.random.random(size=(100, 1))
ax.plot(x, y)
If you run this, you can't then use j,k to move up and down cells until you exit the interactive plot.
This is just a code snippet, in the actual code I am updating the plot from within a loop which is why I'm using interactive mode.
You can add another short-key to close the plot. This can be accomplished via the rcParams.
So let's say you want to close the plot by pressing q.
%matplotlib notebook
import matplotlib.pyplot as plt
plt.rcParams["keymap.quit"] = "ctrl+w", "cmd+w", "q"
plt.plot([1,3,2])
Now pressing q will exit the interactive plot, such that you can navigate as usual through the notebook. E.g. to then get to the next cell, which would already be active, just press Enter.
Eventually you would probably want to change your matplotlib rc file with this option not to have to type it in every time you start a notebook.
I am trying to use the ipython in canopy with matplotlib to prepare graphs (backend set to qt). I wrote the following code line by line int the terminal
import matplotlib.pyplot as plt
fig = plt.figure()
s = fig.add_subplot(1,1,1)
after the second line I can see the figure being made. However after the third line I do not see the sub plot being created. However If I print fig, the sub-plot is can be seen both inline and in the figure window created. This sub-plot also magically appears if I try to zoom. Similar thing happens every time i plot something on the figure. The old version is displayed till I either print the figure or if i try to modify the view using the GUI tools. This is really annoying. It would be great if someone could tell me where the problem is.
Edit: Tried using fig.show() which does not work. Also when I use the plt.plot() directly, there seems to be no problem. The problem comes only when i use fig or any of its subplots
type:
fig.show() when you need to update it.
you should try using fig.canvas.draw() instead of using fig.show() when it comes to interactive plots.
import matplotlib.pyplot as plt
fig = plt.figure()
fig.show()
## should show an empty figure ##
s = fig.add_subplot(1,1,1)
fig.show()
## things stay unchanged ##
fig.canvas.draw()
## things should be OK now ##
Say that I have two figures in matplotlib, with one plot per figure:
import matplotlib.pyplot as plt
f1 = plt.figure()
plt.plot(range(0,10))
f2 = plt.figure()
plt.plot(range(10,20))
Then I show both in one shot
plt.show()
Is there a way to show them separately, i.e. to show just f1?
Or better: how can I manage the figures separately like in the following 'wishful' code (that doesn't work):
f1 = plt.figure()
f1.plot(range(0,10))
f1.show()
Sure. Add an Axes using add_subplot. (Edited import.) (Edited show.)
import matplotlib.pyplot as plt
f1 = plt.figure()
f2 = plt.figure()
ax1 = f1.add_subplot(111)
ax1.plot(range(0,10))
ax2 = f2.add_subplot(111)
ax2.plot(range(10,20))
plt.show()
Alternatively, use add_axes.
ax1 = f1.add_axes([0.1,0.1,0.8,0.8])
ax1.plot(range(0,10))
ax2 = f2.add_axes([0.1,0.1,0.8,0.8])
ax2.plot(range(10,20))
With Matplotlib prior to version 1.0.1, show() should only be called once per program, even if it seems to work within certain environments (some backends, on some platforms, etc.).
The relevant drawing function is actually draw():
import matplotlib.pyplot as plt
plt.plot(range(10)) # Creates the plot. No need to save the current figure.
plt.draw() # Draws, but does not block
raw_input() # This shows the first figure "separately" (by waiting for "enter").
plt.figure() # New window, if needed. No need to save it, as pyplot uses the concept of current figure
plt.plot(range(10, 20))
plt.draw()
# raw_input() # If you need to wait here too...
# (...)
# Only at the end of your program:
plt.show() # blocks
It is important to recognize that show() is an infinite loop, designed to handle events in the various figures (resize, etc.). Note that in principle, the calls to draw() are optional if you call matplotlib.ion() at the beginning of your script (I have seen this fail on some platforms and backends, though).
I don't think that Matplotlib offers a mechanism for creating a figure and optionally displaying it; this means that all figures created with figure() will be displayed. If you only need to sequentially display separate figures (either in the same window or not), you can do like in the above code.
Now, the above solution might be sufficient in simple cases, and for some Matplotlib backends. Some backends are nice enough to let you interact with the first figure even though you have not called show(). But, as far as I understand, they do not have to be nice. The most robust approach would be to launch each figure drawing in a separate thread, with a final show() in each thread. I believe that this is essentially what IPython does.
The above code should be sufficient most of the time.
PS: now, with Matplotlib version 1.0.1+, show() can be called multiple times (with most backends).
I think I am a bit late to the party but...
In my opinion, what you need is the object oriented API of matplotlib. In matplotlib 1.4.2 and using IPython 2.4.1 with Qt4Agg backend, I can do the following:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1) # Creates figure fig and add an axes, ax.
fig2, ax2 = plt.subplots(1) # Another figure
ax.plot(range(20)) #Add a straight line to the axes of the first figure.
ax2.plot(range(100)) #Add a straight line to the axes of the first figure.
fig.show() #Only shows figure 1 and removes it from the "current" stack.
fig2.show() #Only shows figure 2 and removes it from the "current" stack.
plt.show() #Does not show anything, because there is nothing in the "current" stack.
fig.show() # Shows figure 1 again. You can show it as many times as you want.
In this case plt.show() shows anything in the "current" stack. You can specify figure.show() ONLY if you are using a GUI backend (e.g. Qt4Agg). Otherwise, I think you will need to really dig down into the guts of matplotlib to monkeypatch a solution.
Remember that most (all?) plt.* functions are just shortcuts and aliases for figure and axes methods. They are very useful for sequential programing, but you will find blocking walls very soon if you plan to use them in a more complex way.
Perhaps you need to read about interactive usage of Matplotlib. However, if you are going to build an app, you should be using the API and embedding the figures in the windows of your chosen GUI toolkit (see examples/embedding_in_tk.py, etc).
None of the above solutions seems to work in my case, with matplotlib 3.1.0 and Python 3.7.3. Either both the figures show up on calling show() or none show up in different answers posted above.
Building upon #Ivan's answer, and taking hint from here, the following seemed to work well for me:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1) # Creates figure fig and add an axes, ax.
fig2, ax2 = plt.subplots(1) # Another figure
ax.plot(range(20)) #Add a straight line to the axes of the first figure.
ax2.plot(range(100)) #Add a straight line to the axes of the first figure.
# plt.close(fig) # For not showing fig
plt.close(fig2) # For not showing fig2
plt.show()
As #arpanmangal, the solutions above do not work for me (matplotlib 3.0.3, python 3.5.2).
It seems that using .show() in a figure, e.g., figure.show(), is not recommended, because this method does not manage a GUI event loop and therefore the figure is just shown briefly. (See figure.show() documentation). However, I do not find any another way to show only a figure.
In my solution I get to prevent the figure for instantly closing by using click events. We do not have to close the figure — closing the figure deletes it.
I present two options:
- waitforbuttonpress(timeout=-1) will close the figure window when clicking on the figure, so we cannot use some window functions like zooming.
- ginput(n=-1,show_clicks=False) will wait until we close the window, but it releases an error :-.
Example:
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots(1) # Creates figure fig1 and add an axes, ax1
fig2, ax2 = plt.subplots(1) # Another figure fig2 and add an axes, ax2
ax1.plot(range(20),c='red') #Add a red straight line to the axes of fig1.
ax2.plot(range(100),c='blue') #Add a blue straight line to the axes of fig2.
#Option1: This command will hold the window of fig2 open until you click on the figure
fig2.waitforbuttonpress(timeout=-1) #Alternatively, use fig1
#Option2: This command will hold the window open until you close the window, but
#it releases an error.
#fig2.ginput(n=-1,show_clicks=False) #Alternatively, use fig1
#We show only fig2
fig2.show() #Alternatively, use fig1
As of November 2020, in order to show one figure at a time, the following works:
import matplotlib.pyplot as plt
f1, ax1 = plt.subplots()
ax1.plot(range(0,10))
f1.show()
input("Close the figure and press a key to continue")
f2, ax2 = plt.subplots()
ax2.plot(range(10,20))
f2.show()
input("Close the figure and press a key to continue")
The call to input() prevents the figure from opening and closing immediately.