Matplotlib Event handling - python

I was playing with this example code for a while now.
http://matplotlib.org/examples/event_handling/poly_editor.html
I want to get a editable line instead of a polygon. I got it done by
xs = np.arange(0,1,0.1)
ys = xs*2.0
but still im getting a filled polygon. How can i just get a line ?
Output
I want to add a button to this so that when I press the button updated x,y pairs will be printed.
How can I get this done.
Thanks much in advance.

Use this statement in place of the similar one in the code.
poly = Polygon(list(zip(xs, ys)), animated=True, fc='w')
I can't help with adding a button. However, you could put the part of the code that creates the plot in a loop that prints what you want after plt.show(), and includes an input statement offering the user another opportunity to use the facility before exiting.

Related

Matplotlib Patches not matching supplied arguments.

been struggling with python and the matplotlib module. I am trying to draw some circles that are not filled and outlined in black. I am putting the correct arguments in the artist circle but it seems to ignore it and put blue. any ideas? Also the figure shows up automatically without me stating draw or show. How can i block that and control when the graph pops up? Thanks in advance.
my code
def draw_lattice(self,chart):
patches = []
for x in range(1,4):
for y in range (1,4):
circle = Circle((x,y), .25,color='k',fill=False)
# chart.add_patch(circle)
patches.append(circle)
p = PatchCollection(patches)
chart.add_collection(p)
Thanks in advance.
* UPDATE *
if i add each circle individually to the axes it will be formatted properly. If i add the collection it does not work. I have many more shapes to add and i would like to go the collection route. Any reason why one way would work and another wouldn't? I read somewhere that you need to add the artist but i tried that and got an error.
This is a rather late answer but I just came across the same problem and here is how to solve it:
What you need to do is tell the PatchCollection to match the original patches. To do this simply add match_original=True, like so:
p = PatchCollection(my_patches, match_original=True)
You can set the color of the circles when you create the patch collection:
p = PatchCollection(patches,facecolors='w',edgecolor='k')
From the Collection documentation:
"If any of edgecolors, facecolors, linewidths, antialiaseds are None, they default to their matplotlib.rcParams patch setting, in sequence form."

Plot in python that updates on keyboard hit

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!

Get pixel location from mouse click in TKinter

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.

Passing pyplot points as arguments

So I initialised a pyplot figure
import ... ## import all relevent modules
f = plt.figure(figsize=(8,3),dpi(100)
a = plt.subplot(111)
a.set_xlim(left=0,right=25,auto=False)
a.set_ylim(bottom=0,top=250,auto=False)
a.plot([5,10,15],[80,150,210])
plt.show()
This works fine... What I want to be able to do is to write a function that can update the scatter plot dynamically... Something like:
def plot_point(x_coord,y_coord):
a.plot([x_coord],[y_coord])
a.draw() ## I thought this would work... :(
No error, but the point doesn't get plotted. How can I get around this? The reason I've done it using figures is so I can embed it in Tkinter.
Thanks for your help!
plot is perfectly fine to use for plotting individual points (it is even recommend over scatter, if you don't wanna add additional information through color or size of the dots). What is missing in the initial example is setting the right linestyle; obviously, a line consisting of a single point doesn't show up. Changing the line style to '+' or something similar fixes the problem:
def plot_point(x_coord,y_coord):
a.plot([x_coord],[y_coord], '+')

matplotlib interactive graphing (manually drawing lines on a graph)

I have succesfully plotted a set of date sequenced data (X axis is date) using matplotlib. However, I want to be able to manually draw lines from one (date1, y1) to another (date2, y2) on the plotted graph.
I can't seem to find any examples that show how to do this - or indeed if it is even posible.
To summarize, this is what I want to do:
Draw a set of lines on the plotted graph
Save the manually drawn line data to file
Load the manually drawn line data from file (to recreate the graph)
Ideally, I would like to store 'meta data' about the drawn lines (e.g. color, line-width etc)
Can someone post a skeleton snippet (preferably with links to further info), to show how I may get started with implementing this (the main requirements being the ability to manually draw lines on a graph and then to save/load the lines into a plot).
Note: By 'manually', I mean to be able to draw the lines by clicking on a point, and then clicking on another point in the plotted graph. to draw a line between the two points (or simply clicking on a point and dragging and releasing the mouse at another point on the plotted graph)
[[Update]]
dawe, thanks very much for the snippet you provided. This allows me to do what I am trying to do - however, as soon as the line is drawn on the canvas (after the second mouse click), the GUI crashes and I get this warning message on the console:
/usr/local/lib/python2.6/dist-packages/matplotlib/backend_bases.py:2192: DeprecationWarning: Using default event loop until function specific to this GUI is implemented
warnings.warn(str,DeprecationWarning)
Do you know what is causing this warning and the abrupt program termination?
Also, is it possible to draw more than one line on the graph? (I'm guessing this will involve writing some kind of event handler that will instantiate a linedrawer variable). At the moment , I get the chance to draw only one line before the 'app' abruptly terminates.
I would write something like this:
import matplotlib.pyplot as plt
class LineDrawer(object):
lines = []
def draw_line(self):
ax = plt.gca()
xy = plt.ginput(2)
x = [p[0] for p in xy]
y = [p[1] for p in xy]
line = plt.plot(x,y)
ax.figure.canvas.draw()
self.lines.append(line)
Using ginput() you can avoid more complicated event handling. The way it 'works' is you plot something:
plt.plot([1,2,3,4,5])
ld = LineDrawer()
ld.draw_line() # here you click on the plot
For saving/loading the line data to a file you can easily implement a method using pickle or shelve. You can also pass the necessary metadata by the method draw_line()

Categories