ivan199415
ivan199415

Reputation: 438

Getting coordinates of a mouse click in a tkinter-embedded matplotlib graph

I have a matplotlib graph embedded in a tkinter interface with FigureCanvasTkAgg and the entire thing is within a class.

I want to get a data coordinate of a mouse click (the purpose would be to place a point on that mouse click) and so far nothing works. I've tried two options: using .bind directly on widget of canvas, and using figure.canvas.mpl_connect.

If I go with the first option, using .bind on widget of canvas, I can access event.x and event.y, but not event.xdata and event.ydata - which is understandable. Then I need to transform event.x and event.y with ax.transData.inverted().transform(()) - the problem here is that Y coordinate is inverted because in widget, y coordinate increases downwards, while in figure the y coordinate increases upwards. Eg. 0.6 where 0.4 should be. Also, behaves funky when I click outside of axes.

If I go with second option, using figure.canvas.mpl_connect, which should get me straight data coordinate with event.xdata and event.ydata, nothing happens.

Code is below:

Imports

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from matplotlib.figure import Figure
import matplotlib

Function that creates a figure, and returns it:

def figure_create():
 fig=Figure(figsize=(10,10))
 ax=fig.add_subplot(111)
 return(fig)

My class

class GToplevel()   
    
 def __init__(self,data=None):
    self.master=tk.Toplevel()
    figure=figure_create()  #creates a figure
    canvas=FigureCanvasTkAgg(figure,master=self.master) #creates a canvas
    self.figure=figure
    self.canvas=canvas
    widget=canvas.get_tk_widget()
    widget.pack()
    canvas.draw() #draws canvas
    
    #FIRST OPTION
    #widget.bind("<Button-1>",self.click_coordinate) #binds the click to a function
    #Y coordinate is inverted
    
    #SECOND OPTION
    #figure.canvas.mpl_connect('button_press_event', self.click_coordinate)
    #nothing seems to happen

 def click_coordinate(self,event):
    
    x=event.x #x coordinate of event, not Data
    y=event.y #y coordinate of event, not Data
    print(x,y)  #checking that the coordinates are right
    canvas=self.canvas
    figure=self.figure
    ax=figure.get_axes()[0] #the only subplot of a figure
  
    inv = ax.transData.inverted() 
    x,y=inv.transform((x,y))      #turns x,y into Data coordinates
    
    canvas.draw()

Upvotes: 3

Views: 971

Answers (1)

ivan199415
ivan199415

Reputation: 438

The answer was outside of class. Rewriting code:

class GToplevel(): 

def __init__(self,data=None):
    self.master=tk.Toplevel()
    figure=figure_create()  #creates a figure
    canvas=FigureCanvasTkAgg(figure,master=self.master) #creates a canvas
    self.figure=figure
    self.canvas=canvas
    widget=canvas.get_tk_widget()
    widget.pack()
    canvas.draw() #draws canvas

    figure.canvas.callbacks.connect('button_press_event', self.callback)

def callback(self,event):
    x=event.xdata
    y=event.ydata
    print(x,y)

I can access data coordinates directly by using event.xdata and event.ydata, but figure.canvas.callbacks.connect must be called. This didn't work, because my main code was :

root=tk.Tk()
GToplevel()
root.mainloop()

But when I modified second line like this:

g=GToplevel()

Everything worked.

I have no idea why is that so, but this fixes my question.

Upvotes: 1

Related Questions