【问题标题】:How do I display the value of a heatmap element by hovering the cursor over it?如何通过将光标悬停在热图元素上来显示热图元素的值?
【发布时间】:2019-11-28 09:12:03
【问题描述】:

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

我已经让它显示热图的值,但它也显示了我不想要的信息,当我第一次运行程序时,有很多错误,我不知道为什么。

我尝试了各种方法来显示我在网上看到的值,例如datacursor(hover=True),但mplcursors.cursor(hover=True) 是唯一“有效”的方法。

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 坐标,但我不确定如何删除/自定义显示的信息,如果我在运行程序时没有错误,我希望它(即使它在技术上确实有效)。

【问题讨论】:

    标签: python matplotlib tkinter mplcursors


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      您可以连接到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打开/关闭注解。

      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()
      

      【讨论】:

      • 我添加了Checkbutton的示例
      【解决方案3】:

      您需要决定是使用 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()
      

      【讨论】:

      • 有没有办法在创建后禁用悬停框?例如,如果使用tk.Checkbutton?我尝试将if checkbuttonvariable==0: 添加到if event.inaxex==ax: 行中,方法是与它们匹配,但这不起作用。然后我尝试设置annot.set_visable(False),但这也没有用。能做到吗?
      • @MRB in hover 使用 print(checkbuttonvariable)print(type(checkbuttonvariable)) 看看你得到了什么。也许你得到字符串"0",而不是整数0
      • @ImportanceOfBeingErnest 我在尝试代码时注意到您建议热图值被反转并且与原始数组不匹配?就好像它们从右上角翻转到左下角一样?
      • @MBR 将[j,i] 更改为[i,j],您会得到正确的值。
      猜你喜欢
      • 1970-01-01
      • 2019-11-14
      • 1970-01-01
      • 2016-02-29
      • 2013-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多