I've been struggling to achieve something that is trivial in Octave: to produce a series of plots, that change when I hit a key. Here's my octave code sample.
x = [1:10];
for c=1:3
plot(x,c.*x);
hold off;
input('a');
end
When I try to do the same in python, I realized that python matplotlib has the save function, which puts it in non-blocking mode, and so I have to close the figure using a mouse, for the next figure to be produced. And the next figure is at a random other location on the screen. How can I get python to imitate the above behavior? I've tried various combinations of ion(), ioff(), plt.show(), plt.draw(), but haven't succeeded.
You can do something fancier if you want, using mpl_connect
First import pylab
from pylab import *
Then define an updateData function that can be connected to the figure canvas.
i = 1
def updateData(event):
global i,x
i +=1
y = i*x
data.set_data(x,y)
ylim(y[0],y[-1])
draw()
i and x are global variables in this case. (This could be treated in better way, this is just an example!)
Then, create you plot and connect with your defined function.
f = figure()
data, = plot(x,x)
i=1
f.canvas.mpl_connect("key_press_event",updateData)
show()
Whenever you hit any key in the keyboard (When the figure window is selected) the function updateData is called, i is incremented and the plot updated.
Have fun!
Related
What I want to do is to dynamically change a plot so that I can see it update as Python is executing its code. Here is what I've come up with:
import matplotlib.pyplot as plt
import time
def plotResult(x,y):
plt.plot(x,y)
plt.figure()
for i in range(5):
x = [2,3,5*i]
y = [1,2,3]
plotResult(x,y)
time.sleep(1)
What I want is for each call of "plotResult" to erase the previous plot with the new plot in its place. What I end up with instead is each plot on top of each other. I'm using time.sleep here because I want some time to look at the newly plotted result before it gets erased and replaced with a new plot. I guess I'm essentially trying to create an animation here with each frame being a call to plotResult.
I'm going to do this for a code with a much longer execution time, so I don't want to have to wait until the code is done being executed to view the animation. Please let me know if you know of a way to do this.
Read about the FuncAnimation class. It repeatedly calls a function to update each frame of the animation.
I register 2 callbacks - 1 for clicking and 1 for picking.
import matplotlib.pyplot as plt
fig = plt.figure()
fig.canvas.mpl_connect('button_release_event', onclick)
fig.canvas.mpl_connect('pick_event', onpick)
...
def onclick(event): ...
def onpick(event): ...
I wanted to know in my click event whether or not it occurs with a pick event, (knowing that clicking on a pickable object triggers both events). I noticed that the pick event always comes first, so I check / set a variable like clicked_point = True # or False in onpick and onclick.
This works fine so far but seems sketchy. I couldn't find an answer in the docs, but perhaps I have simply have not found the right section yet.
My alternative would be to only register 'button_release_event', and calculate Euclidean distances between click coordinates and data points on my scatter plot to determine if the 'pick_event' behavior should be evoked. I'd rather rely on a call order for these functions registered with mpl_connect if it does indeed exist.
I've come across code that checks if an event is in a set of artists (ax.collections.contains(event) or something similar). However, since I use a single call to scatter with the full array of data points (plt.scatter(X, Y, ..)), I am returned a single PathCollection object, which I understand represents a path for all the points in the order they came in. However I could never quite figure out to get indivual artists from this collection, so when I check for event containment in [PathCollection], it always returns false. Not sure if something like this was even doable or if it makes sense, but I figured I'd document it here in case it was relevant.
I'm working with matplotlib plotting and use ioff() to switch interactive mode off to suppress the automatic opening of the plotting window on figrue creation. I want to have full control over the figure and only see it when explicitely using the show() command.
Now apparently the built-in commands to clear figures and axes do not work properly anymore.
Example:
import numpy as np
import matplotlib.pyplot as mpp
class PlotTest:
def __init__(self,nx=1,ny=1):
# Switch off interactive mode:
mpp.ioff()
# Create Figure and Axes:
self.createFigure(nx, ny)
def createFigure(self,nx=1,ny=1):
self.fig, self.axes = mpp.subplots(nx,ny)
if nx*ny == 1:
self.axes = np.array([self.axes])
def linePlot(self):
X = np.linspace(0,20,21)
Y = np.random.rand(21)
self.axes[0].plot(X,Y)
P = PlotTest()
P.linePlot()
P.fig.show()
Now I was thinking I could use P.fig.clear() any time to simply clear P.fig, but apparently that's not the case.
Writing P.fig.clear() directly into the script and execute it together it works and all I see is an empty figure. However that's rather pointless as I never get to see the actual plot like that.
Doing P.fig.clear() manually in the console does not do anything, regardless if the plot window is open or not, all other possible commands fail as well:
P.fig.clf()
P.axes[0].clear()
P.axes[0].cla()
mpp.clf()
mpp.cla()
mpp.close(P.fig)
Wrapping the command into a class method doesn't work either:
def clearFig(self):
self.fig.clear()
EDIT ================
After a clear() fig.axes is empty, yet show() still shows the old plot with the axes still being plotted.
/EDIT ================
Is it because I switched off interactive mode?
If you add a call to plt.draw() after P.fig.clear() it clears the figure. From the docs,
This is used in interactive mode to update a figure that has been altered, but not automatically re-drawn. This should be only rarely needed, but there may be ways to modify the state of a figure with out marking it as stale. Please report these cases as bugs.
I guess this is not a bug as you have switched off interactive mode so it is now your responsibility to explicitly redraw when you want to.
You can also use P.fig.canvas.draw_idle() which could be wrapper in the class as clearFigure method.
I am new to Python, and somewhat new to object oriented programming. Can anyone explain what is going on and how things are typically done with a matplotlib GUI callback? I've taken the "event_handling example code" from the Matplotlib website and stripped it down for clarity. When you run this code it makes a plot, and if you press a key on the keyboard the press function is called. The press function is passed only event, but somehow every other variable from main program level appears inside the call to press but as a global variable, is this normal for functions? I can print the value of x, but if I try to change it then it makes a local variable version, worse yet now I have seemingly no way to access the global version anymore?
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
x=np.random.rand(3)
y=np.random.rand(3)
def press(event):
print(x)
print('Local Var:', locals().keys())
print('Global Var:', globals().keys())
fig, ax = plt.subplots()
fig.canvas.mpl_connect('key_press_event', press)
ax.plot(x,y)
plt.show()
I have searched and had quite a hard time finding any reference that explains how to access or properly pass useful data in and out of the callback function so that a GUI event can do something useful, like update some data or feature of a plot?
So lets say I wanted to have the callback function modify y and re-plot the data. How is that typically done?
you have global access to x inside your callback, but can't modify it unless you specify it global.
def press(event):
global x
...
locals().keys() and globals().keys() are printing namespaces; I am unsure why you need to do that.
Your callback receives an event that you can use and manipulate inside the function.
Here is an example:
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
x=np.random.rand(3)
y=np.random.rand(3)
def press(event):
print(event, event.key)
fig, ax = plt.subplots()
fig.canvas.mpl_connect('key_press_event', press)
ax.plot(x,y)
plt.show()
click on the plot window to set the focus.
pressing f should print the event object and set the plot full screen
pressing f again, will print f and restore the size of the window
pressing s will print s and will offer to save your figure
etc...
To learn more about how you can manipulate events, look up backend_bases in the very rich matplotlib web site. For example, you can set mouse_clicks events that allow you to capture canvas coordinates to add points or modify figures...
I'm quite new to Python and have been unsuccessful in finding a way around this problem. I have a GUI using TKinter that displays an image using Label. I would like the user to be able to click on two places in the image and use those two pixel locations elsewhere.
Below is the basic code I'm using so far, but I'm unable to return the pixel locations. I believe bind is not what I want to use, is there another option?
px = []
py = []
def onmouse(event):
px.append(event.x)
py.append(event.y)
return px,py
self.ImgPanel.bind('<button-1>',onmouse)
If I try to use:
px,py = self.ImgPanel.bind('<button-1>',onmouse)
I get an error "Too many values to unpack"
bind is what you want, if you want to capture the x,y coordinate of the click. However, functions called from bindings don't "return". Technically they do, but they return a value to the internals of Tkinter.
What you need to do is set an instance or global variable within the bound function. In the code you included in your question, if you add global px,py, you can then use those values in other code.