Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the location of a mouse click in Matplotlib using Tkinter

I am trying to write a simple GUI in python that displays a plot and then allows the user to click on certain key characteristics (turning points etc), which will then be used as a starting point for a fitting algorithm I'm developing.

I found the following thread to get me started; Store mouse click event coordinates with matplotlib

This only seems to give me the pixel locations of the mouse from my computer's perspective. What I would ideally like is to be able to get at the coordinates on the right hand side of the toolbar and use those; it seems a shame to try to write my own pixel->data transform when matplotlib is clearly already doing it for me somewhere.

Here is the code I have so far, if it looks like I'm approaching the problem in the wrong manner please tell me, I'm not too proud to start over.

import matplotlib
matplotlib.use('TkAgg')

import Tkinter as tk

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.backend_bases import MouseEvent
from matplotlib.figure import Figure

import numpy as np


def callback(event):
    print "clicked at", event.x, event.y

root = tk.Tk()

f = Figure(figsize=(5,4), dpi=100)
a = f.add_subplot(111)
t = np.arange(0.0,3.0,0.01)
s = np.sin(2*np.pi*t)

a.plot(t,s)

canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

canvas.mpl_connect('button_press_event',callback)
def callback(event):
    print "clicked at", event.xdata, event.ydata

toolbar = NavigationToolbar2TkAgg( canvas, root )
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)

toolbar

root.mainloop()
like image 450
jimbarrett27 Avatar asked Dec 11 '22 02:12

jimbarrett27


1 Answers

Edit: I jumped into answering without reading your example completely. The problems you're having are due to the order that you've defined things in. Basically, you have:

import matplotlib.pyplot as plt

def callback(event):
    print event.x, event.y

fig, ax = plt.subplots()

fig.canvas.callbacks.connect('button_press_event', callback)
def callback(event):
    print event.xdata, event.ydata

When you're connecting the callback, the second function hasn't been defined yet, so it's connecting to the earlier function with the same name. Either move the callback connection after the function is defined, or just move the function definition up and delete the (unused) first callback function.

Regardless, have a look at matplotlib's transforms to understand how you'd transform between display/pixel coordinates and plot coordinates. I'll leave my original answer in the hope it helps someone else who comes across this question.


If I'm understanding you correctly, you want the data coordinates where the mouse click occured?

If so, use event.xdata and event.ydata.

As a quick example:

import matplotlib.pyplot as plt

def on_click(event):
    if event.inaxes is not None:
        print event.xdata, event.ydata
    else:
        print 'Clicked ouside axes bounds but inside plot window'

fig, ax = plt.subplots()
fig.canvas.callbacks.connect('button_press_event', on_click)
plt.show()

In general, though, have a look at matplotlib's transforms for transforming between display, figure, axes, and data coordinates.

For example, the eqivalent of event.xdata and event.ydata would be:

x, y = event.inaxes.transData.inverted().transform((event.x, event.y))

It's a bit verbose, but ax.transData is the transformation between display (pixel) coordinates and data coordinates. We want to go the other way, so we invert the transformation with trans.inverted(). Transforms deal with more than just points (usually you use them to plot something in another coordinate system), so to convert a set of points, we need to call the transform method.

It seems cumbersome at first, but it's actually rather elegant. All plotted artists take a transfom argument that defines the coordinate system they're drawn in. This is how things like annotate allow a location defined by an offset in points from another location in data coordinates.

like image 181
Joe Kington Avatar answered Jan 19 '23 00:01

Joe Kington