如何通过将光标悬停在热图元素上来显示热图元素的值?

How do I display the value of a heatmap element by hovering the cursor over it?

我想在将鼠标悬停在热图的特定元素上时显示它的值。

我已经得到它来显示热图的值,但它也显示了我不想要的信息,当我第一次 运行 该程序时,有很多错误,我无法弄清楚为什么.

我已经尝试过各种显示值的方法,例如datacursor(hover=True),但是mplcursors.cursor(hover=True)是唯一'works'。

import tkinter as tk                                                    
from tkinter import ttk
from tkinter import messagebox
import numpy as np
from math import pi
import random
import matplotlib.pyplot as plt

from mpldatacursor import datacursor
import mplcursors

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


import pandas as pd
from openpyxl import load_workbook
from tkinter import filedialog

root = tk.Tk()                                                          
root.title("Stage 2 GUI")                                               
root.geometry("1270x590")  

mapArr = np.zeros([2,2],dtype=float)

mapArr=([113,62,31,63],
        [50,101,72,47],
        [92,10,40,12],
        [83,21,128,16])

xy=(['A','B','C','D','E','F'])


figure, axis = plt.subplots(figsize=(8,7))                              
heatmap = axis.imshow(
    mapArr,cmap="gray",interpolation='nearest',vmin=0, vmax=128)        

heatmap.axes.get_xaxis().set_visible(False)                             
heatmap.axes.get_yaxis().set_visible(False)

cb = figure.colorbar(heatmap)                                           

canvas = FigureCanvasTkAgg(figure, root)                                
canvas.get_tk_widget().place(x=-60,y=-60)                               

mplcursors.cursor(hover=True)

plt.show()

我想显示热图元素的值而不是 x 和 y 坐标,但我不确定如何删除/自定义显示的信息,如果我 运行 程序(即使它在技术上确实有效)。

你总是可以使用袖扣和情节,我相信他们已经内置了这个功能:https://plot.ly/ipython-notebooks/cufflinks/

您可以连接到 coursor 函数,这将更改注释中的文本

cursor = mplcursors.cursor(hover=True)

@cursor.connect("add")
def on_mouse_move(sel):
    x = sel.target[0]
    y = sel.target[1]

    value = sel.artist.get_cursor_data(_Event(x, y))

    sel.annotation.set_text("value {} at ({:1.2f}, {:1.2f})".format(value, x,y))

要获得它需要的值 namedtuple(我在 Cursor 的源代码中找到它)

from collections import namedtuple

_Event = namedtuple('_Event', 'xdata ydata')

完整的工作示例(只需要代码,所以没有错误)

import tkinter as tk                                                    
import matplotlib.pyplot as plt
import mplcursors
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# --- data ---

mapArr = (
    [113, 62, 31, 63],
    [50, 101, 72, 47],
    [92, 10, 40, 12],
    [83, 21, 128, 16]
)

xy = ('A','B','C','D','E','F')

# --- main ---

root = tk.Tk()                                                          

figure, axis = plt.subplots(figsize=(8, 7))

canvas = FigureCanvasTkAgg(figure, root)                                
canvas.get_tk_widget().pack(fill='both', expand=True)

heatmap = axis.imshow(mapArr, cmap="gray", interpolation='nearest', vmin=0, vmax=128)        
heatmap.axes.get_xaxis().set_visible(False)                             
heatmap.axes.get_yaxis().set_visible(False)

colorbar = figure.colorbar(heatmap)                                           

#--- connect function to cursor

from collections import namedtuple
_Event = namedtuple('_Event', 'xdata ydata')

cursor = mplcursors.cursor(hover=True)

@cursor.connect("add")
def on_mouse_move(sel):
    x = sel.target[0]
    y = sel.target[1]

    value = sel.artist.get_cursor_data(_Event(x, y))

    sel.annotation.set_text("value {} at ({:1.2f}, {:1.2f})".format(value, x,y))

#--- 

root.mainloop()

编辑: 使用@ImportanceOfBeingErnest 示例,我发现我在 canvas.mpl_connect() 版本中的错误在哪里,现在它可以工作了。

这个例子展示了如何使用Checkbutton转on/off注解。

import tkinter as tk                                                    
import matplotlib.pyplot as plt
import mplcursors
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# --- data ---

mapArr = (
    [113, 62, 31, 63],
    [50, 101, 72, 47],
    [92, 10, 40, 12],
    [83, 21, 128, 16]
)

xy = ('A','B','C','D','E','F')

# --- functions ---

def on_mouse_move(event):
    if checkbuttonvariable.get() == 0:
        return

    if event.inaxes == axis:
        annotation.xy = (event.xdata, event.ydata)
        row = int(round(event.ydata))
        col = int(round(event.xdata))
        value = mapArr[row][col]
        annotation.set_text(str(value))
        annotation.set_visible(True)
    else:
        annotation.set_visible(False)
    canvas.draw()

# --- main ---

root = tk.Tk()                                                          

figure, axis = plt.subplots(figsize=(8, 7))

canvas = FigureCanvasTkAgg(figure, root)                                
canvas.get_tk_widget().pack(fill='both', expand=True)

heatmap = axis.imshow(mapArr, cmap="gray", interpolation='nearest', vmin=0, vmax=128)        
heatmap.axes.get_xaxis().set_visible(False)                             
heatmap.axes.get_yaxis().set_visible(False)

colorbar = figure.colorbar(heatmap)

# ---

annotation = axis.annotate("", xy=(0,0), xytext=(20,20), textcoords="offset points",
                    arrowprops=dict(arrowstyle="->"), visible=False,
                    bbox=dict(boxstyle="round", fc="w"))

canvas.mpl_connect('motion_notify_event', on_mouse_move)

# ---

checkbuttonvariable = tk.IntVar(value=1)
button = tk.Checkbutton(root, text='visible', variable=checkbuttonvariable)
button.pack()


root.mainloop()

您需要决定是使用 pyplot 还是将 matplotlib 嵌入到 tk 中。下面假设你想嵌入(在那种情况下,不要使用 pyplot!!)。

使用 mplcursors

mplcursors documentation 解释了如何自定义输出。本质上它包括连接到一个名为 "add".

的事件
import numpy as np
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import mplcursors

root = tk.Tk()
root.geometry("1270x590")  

mapArr=np.array(([113,62,31,63],
                 [50,101,72,47],
                 [92,10,40,12],
                 [83,21,128,16]))

xy=(['A','B','C','D','E','F'])


fig = matplotlib.figure.Figure() 
ax = fig.add_subplot()

heatmap = ax.imshow(mapArr,cmap="gray",interpolation='nearest',vmin=0, vmax=128)        

cb = fig.colorbar(heatmap)                                           

canvas = FigureCanvasTkAgg(fig, root)                                
canvas.get_tk_widget().place(x=60,y=60)                               

cursor = mplcursors.cursor(heatmap, hover=True)

@cursor.connect("add")
def on_add(sel):
    i,j = sel.target.index
    sel.annotation.set_text(mapArr[i,j])

tk.mainloop()

手动创建悬停框

您可以在不使用 mplcursors 的情况下执行与上述相同的操作。这将通过创建注释并根据鼠标位置更改其位置和文本来完成。

import numpy as np
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk

root = tk.Tk()
root.geometry("1270x590")  

mapArr=np.array(([113,62,31,63],
                 [50,101,72,47],
                 [92,10,40,12],
                 [83,21,128,16]))

xy=(['A','B','C','D','E','F'])


fig = matplotlib.figure.Figure() 
ax = fig.add_subplot()

heatmap = ax.imshow(mapArr,cmap="gray",interpolation='nearest',vmin=0, vmax=128)        

cb = fig.colorbar(heatmap)                                           

canvas = FigureCanvasTkAgg(fig, root)                                
canvas.get_tk_widget().place(x=60,y=60)                               

annot = ax.annotate("", xy=(0,0), xytext=(20,20), textcoords="offset points",
                    arrowprops=dict(arrowstyle="->"), visible=False,
                    bbox=dict(boxstyle="round", fc="w"))

def hover(event):
    if event.inaxes == ax:
        x,y = event.xdata, event.ydata
        j,i = np.round(np.array((x,y))).astype(int)
        annot.xy = (x,y)
        annot.set_text(str(mapArr[i,j]))
        annot.set_visible(True)
    else:
        annot.set_visible(False)
    fig.canvas.draw_idle()

canvas.mpl_connect("motion_notify_event", hover)

tk.mainloop()