-->

Use matplotlib pick event to trigger when center o

2019-06-11 15:41发布

问题:

I am working on a broader project, but am running into an issue using matplotlibs picker=True and pick_event with a pcolormesh. It works, but is not intuitive as the pick event is only triggered when i click along the edges of the heatmap 'boxes', and it returns the surrounding indices (so if you click a corner 4 indices are returned by the intersection of the 4 boxes at that point).

For example, in the following picture when I click near the yellow circle my picker returns indices [5 8] (5 is the box below and 8 above). Instead, I'd like to return only index 5 when that 'box' is clicked.

Here is the code I used to exemplify my issue:

import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
#import matplotlib.animation as animation
#from matplotlib import style
import numpy as np

import Tkinter as tk
import ttk

def customplot(f,X):
    try:
        f.clf()
    except:
        None
    try:
        ax=ax
    except:
        ax=f.add_subplot(111)
    ax.pcolormesh(X,picker=True)
    ax.set_yticks(arange(0.5,len(X)+0.5))
    ax.set_xticks(arange(0.5,len(X)+0.5))
    ax.set_yticklabels(["LabY"+str(l) for l in range(len(X))])
    ax.set_xticklabels(["LabX"+str(l) for l in range(len(X))])

class My_GUI:

    def __init__(self,master):
        self.master=master
        self.f = Figure(figsize=(5,5), dpi=100)
        self.canvas1=FigureCanvasTkAgg(self.f,self.master)
        self.canvas1.get_tk_widget().pack(side="top",fill='x',expand=True)
        self.canvas1.mpl_connect('pick_event',self.onpick)
        self.toolbar=NavigationToolbar2TkAgg(self.canvas1,master)
        self.toolbar.update()
        self.toolbar.pack(side='top',fill='x')           
        self.drawcustomplot()

    def drawcustomplot(self):
        self.X=array(np.random.normal(size=[3,3]))
        customplot(self.f,self.X)
        #plt.xticks([1,2,3],['one','two','three'])
        self.canvas1.show()

    def onpick(self,event):
        print('Returned indices')
        print(event.ind)
        print('mapping back:')
        self.myxlabels=["LabX"+str(l) for l in range(len(self.X))]
        self.myylabels=["LabY"+str(l) for l in range(len(self.X))]
        self.ypos=event.ind[0] / 3
        self.xpos=event.ind[0] % 3
        print("Y: "+str(self.myylabels[self.ypos])+'  X:'+str(self.myxlabels[self.xpos]))


root=tk.Tk()
gui=My_GUI(root)
root.mainloop()

this is further to the question found here: Can matplotlib pick_event return array indeces rather than values or pixels?. I'd like to avoid trying to draw an underlying scatterplot that is masked by the pcolormesh, but handles the picker events if possible as I feel there must be a simpler way to achieve the desired result.

回答1:

I would suggest to use a button_press_event here. From that you can easily obtain the coordinates of the clicked pixel. In the case of an imshow plot it's even easier,

x = int(np.round(event.xdata))
y = int(np.round(event.ydata))

Complete example (using imshow):

import numpy as np
import matplotlib.pyplot as plt

X=np.array(np.random.normal(size=[3,3]))

myxlabels=["LabX"+str(l) for l in range(len(X[0,:]))]
myylabels=["LabY"+str(l) for l in range(len(X))]
plt.imshow(X)
plt.xticks(range(len(X[0,:])),myxlabels )
plt.yticks(range(len(X)),myylabels )

def onclick(event):
    print('Returned indices')
    print(event.xdata, event.ydata)
    print('mapping back:')
    x = int(np.round(event.xdata))
    y = int(np.round(event.ydata))
    tx = "Y: {}, X: {}, Value: {:.2f}".format(myylabels[y], myxlabels[x], X[y,x])
    print(tx)

plt.gcf().canvas.mpl_connect('button_press_event', onclick)

plt.show()