Python - Spyder ignoring picker enabled plot - python-3.x

I am writing this script in Spyder (Python 3.5) and I want it to do this:
1) Plot something
2) Allow me to pick some values from the plot
3) Store those values into a variable
4) Do something with that variable
I have checked this thread: Store mouse click event coordinates with matplotlib and modified the function presented there for my own code. The problem I have is that spyder seems to ignore the interactive plot and runs the whole script at once, without waiting for me to pick any values from the plot. As I am using the values for further calculations, I obviously get an error from this. I have even tried to set an input('Press enter to continue...') after the plot, to see if it made it stop and wait for my pickings, but it does not work either.
When I run the script step by step, it works fine, I get the plot, pick my values, print the variable and find all of them in there and use them afterwards. So the question is: how can I make it work when I run the whole script?
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import plot as plot
def onpick(event):
ymouse = event.ydata
print ('y of mouse: {:.2f}'.format(ymouse))
times.append(ymouse)
if len(times)==5:
f.canvas.mpl_disconnect(cid)
return times
#
t=np.arange(1000)
y=t**3
f=plt.figure(1)
ax=plt.gca()
ax.plot(t,y,picker=5)
times=[]
cid=f.canvas.mpl_connect('button_press_event',onpick)
plt.show()
#Now do something with times
mtimes=np.mean(times)
print(mtimes)

(Spyder maintainer here) I think to solve this problem you need to go to
Preferences > IPython console > Graphics
and turn off the option called Activate support. That will make your script to block the console when a plot is run, so you can capture the mouse clicks you need on it.
The only problem is you need to run
In [1]: %matplotlib qt5
before starting to run your code because Spyder doesn't that for you anymore.

Related

Is it possible to update inline plots in Python (Spyder)?

Setup: Anaconda 3 (Win10 64), Spyder 4 and Python 3.7. The IPython Graphics setting is default (Inline).I'm still a new to Python but I've looked around and have not found an answer that solves my problem so far. Thanks everyone in advance.
So in this setup, whenever I create a plot using matplotlib, it appears in the plot pane of Spyder. e.g.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.DataFrame(np.random.randint(0,100,size=(100, 1)), columns=list('A'))
bp = df.boxplot(column = 'A')
creates a boxplot. Now, if I want to add a title to the plot, the code would be
bp.set_title("This Title")
This is where I'm getting some problems. If I run the entire block together
df = pd.DataFrame(np.random.randint(0,100,size=(100, 1)), columns=list('A'))
bp = df.boxplot(column = 'A')
bp.set_title("This Title")
then I get a box plot with "This Title" as the title, showing up in the plot pane,
which is what I want.
However, if I run the above code line by line in the IPython console, the 2nd line will produce a boxplot as expected, but the 3rd line will not have an effect on the image in the plot pane, so the image in the plot pane still do not have a title
Now,if i go to Tools > Preference >IPython Console > Graphics and set the graphics backend to Automatic instead of the default Inline, then when I run the code in the Console line by line, I get an image that pops up in another window, and that it does update/refreshes based on new lines entered into the console. I understand that the inline plots are supposed to be static, but I thought I saw another post where someone said that it is possible to update inline plots? So now my questions are:
Do plots only update/refresh by line codes in the IPython console if the Graphics Backend is not static like inline?
Why do I get different result when I run code blocks vs line by line?
If it is possible to update the inline plots (preferably in the plot pane of Spyder), how do you do it? I've tried various methods to redraw the plots,for example
plt.show()
plt.draw()
bp.get_figure().canvas.draw()
but none of these updates the image in the plot pane. I figured that even if I can't update the image, I should at least be able to redraw it (i.e a 2nd image appears in the plot pane with the update characteristics). But nothing I've tried worked so far. Please advise and thanks again.
(Spyder maintainer here) About your questions:
Do plots only update/refresh by line codes in the IPython console if the Graphics Backend is not static like inline?
Correct.
Why do I get different result when I run code blocks vs line by line?
Because when you run code cells (which is what I think you mean by "code blocks") your plot is shown at the end of that code and hence it takes all modifications you've done to it in intermediate lines.
If it is possible to update the inline plots (preferably in the plot pane of Spyder), how do you do it?
No, it's not possible. As you correctly mentioned above, inline plots are static images, so they can't be modified.

Suppress plot window when creating figure

For years I have created figures using some rendition of the following code:
import pylab as pl
fig = pl.figure(figsize=(3.5, 2.5))
ax0 = fig.add_subplot(111)
Earlier this week I had to reinstall Python using the most-recent Anaconda Python 3.7 release (previous version was also 3.7). The problem is when I create my figure the figure window appears immediately (normally the figure window would never appear until I called fig.show()). I checked the matplotlib backend to see if it was different, but it is set to qt5agg. I don't think the backend is the problem. Does anyone know of a recent change in matplotlib that would cause the figures to pop up...anyone know how to prevent them from popping up without setting the backent to agg?
Current matplotlib version: 3.1.1

Store Click Coordinates Using Matplotlib

I am trying to display an image, click on it somewhere, then store those coordinates into a variable. However, I am unable to do so. I can print the click coordinates no problem, but I cannot figure out a way to actually store those coordinates. The matplotlib documentation has some tutorials on how to use "fig.canvas.mpl_connect" in general, but none of the routines cover storing the click coordinates, which is what I want to do. There are some tutorials on StackExchange, as well as other websites, but they seem to be for outdated versions of python and/or matplotlib.
Here is my simple code right now:
import matplotlib.pyplot as plt
x = 0
def onclick(event):
print(event.xdata)
print(event.ydata)
global x
x = event.xdata
fig, ax = plt.subplots(figsize=(8,8))
plt.show()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
print(x)
Upon running this code, it immediately prints '0', THEN displays the image. When I then click on that figure, I get coordinates printed to the console. I have tried putting a pause command before the print statement, but it just waits to print '0', then displays the image. Essentially, I need it to display the image so I can click it, THEN print the coordinates of my click.
Any help would be appreciated. I am also open to another method of obtaining the click coordinates, if one exists. Thank you.

is it possible to edit matplotlib plot interactively?

I am not sure if this is an acceptable question in SE.
I am wondering if it is possible to edit matplotlib plot interactively. i.e.,
# plot
plt.plot(x, y[1])
plt.plot(x, -1.0*y[2])
plt.show()
will open up a tk screen with the plot. Now, say, I want to modify the linewidth or enter x/y label. Is it possible to do that interactively (either on the screen, using mouse like xmgrace or from a gnuplot like command prompt)?
You can do simple interactive editing with pylustrator
pip install pylustrator
One way to do what (I think) you ask for is to use ipython. ipython is an interactive python environment which comes with many python distributions.
A quick example:
In a cmd, type >>> ipython, which will load the ipython terminal. In ipython, type:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
ax.plot([1, 2, 3, 4, 5], [1, 2, 3, 4, 5], 'r-')
fig.show()
Now you have a figure, at the same time as the ipython terminal is "free". This means that you can do commands to the figure, like ax.set_xlabel('XLABEL'), or ax.set_yticks([0, 5]). To make it show on screen, you need to redraw the canvas, which is done by calling fig.canvas.draw().
Note that with ipython, you have full tab-completion with all functions to all objects! Typing fig.get_ and then tab gives you the full list of functions beginning with fig.get_, this is extremely helpful!
Also note that you can run python-scripts in ipython, with run SCRIPT.py in the ipython-cmd, and at the same time having access to all variables defined in the script. They can then be used as above.
Hope this helps!
No, it is not generally possible to do what you want (dynamically interact with a matplotlib using the mouse).
What you see is a rendering of your plot on a "canvas", but it does not include a graphical user interface (GUI) like you have with e.g. xmgrace, Origin etc.
That being said, if you wish to pursue it you have a number of possible options, including:
Modify the matplotlib source code yourself to include a GUI
Do something with buttons, like in YuppieNetworking's answer here:
Change dynamically the contents of a matplotlib plot
But it is probably quicker and more convenient to just use some other plotting software, where someone has already designed a decent user interface for you.
Alternatively, using an iPython notebook to quickly modify your plot script works well enough.
There is a navigation toolbar in qt4agg matplotlib backend which you can add easily. Not much, but at least good scaling...
Not a working code, just some fragments:
from matplotlib.backends.backend_qt4agg import FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
self.figure = Figure(figsize=(5, 3))
self.canvas = FigureCanvas(self.figure)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(self.canvas, self))
Self is your window object derived from QtGui.QMainWindow.

Matplotlib Crash When Figure 1 not Closed Last

I am plotting mutliple figures using Matplotlib using Python 3.4.
When the multiple figures are open and I close the windows closing the first figure last (ie once all other figures are closed) python does not crash.
If, however, I close the first figure that was plotted first and then close the rest Python crashes.
It seems as though you need to close the windows in such an order that the first window that was opened must be closed last. Has anyone else experienced and is there a solution?
Here is a trivial example code that can be used to verify:
import matplotlib.pyplot as plt
plt.figure(1) # the first figure
plt.plot([1,2,3])
plt.figure(2) # a second figure
plt.plot([4,5,6])
plt.show()
As discussed on the IPython bug tracker this is a bug in a TCL/TK library which is shipped with python 3.4 on windows.
Changing the backend to Qt works around the problem by using a different gui framework.
The way I have managed to solve this issue has been to use Qt4 as the matplotlib backend.
Simply add the following two lines of code after importing matplotlib.
import matplotlib as mpl
mpl.rcParams['backend'] = "qt4agg"
mpl.rcParams['backend.qt4'] = "PySide"
This is what I do on Python 3 and have no plot closing errors
I use this to do effectively what should be done by plt.close('all'):
def closeall(): # this closes all figures in reverse order
l = plt.get_fignums()
l.reverse()
for a in l:
plt.close(a)
Whenever you are plotting multiple figures, do not use plt.show, create your figures separately in a figure instance and add an Axes using add_subplot. Here is an example:
import matplotlib.pyplot as plt
fig1 = plt.figure()
ax1 = fig1.add_subplot(211) # the first subplot in the first figure
ax1.plot([1,2,3])
ax2 = fig1.add_subplot(212) # the second subplot in the first figure
ax2.plot([4,5,6])
plt.suptitle('Easy as 1,2,3')
fig1.show()
fig2 = plt.figure()
ax3 = fig2.add_subplot(211) # the first subplot in the second figure
ax3.plot([4,5,6])
ax4 = fig2.add_subplot(212) # the second subplot in the second figure
ax4.plot([4,5,6])
plt.suptitle('Easy as 1,2,3')
fig2.show()
By doing so you can still use your python shell even when the plots are active. This is the best way to plot multiple plots.

Resources