如何在悬停时注释broken_barh图表?

问题描述 投票:0回答:2

我正在尝试使用 python 中的 matplotlib 绘制甘特图,其中不同算法建议了两种解决方案。每个算法的解决方案都包含一组在不同时间点开始和结束的批次(以不同颜色显示)。

我可以绘制相同的图,但我想以这样的方式注释图表,每当我将鼠标悬停在解决方案上时,它都会显示批次详细信息或条形长度(处理时间)。我尝试了多种方法,但没有发生。 [当我将鼠标移到批处理解决方案上时,我希望看到 (x,y)=(批处理时间,算法名称)值。

import matplotlib.pyplot as plt 
import numpy as np
import pandas as pd

%matplotlib notebook
  
 
fig, gnt = plt.subplots() 
gnt.set_ylim(0, 50)
gnt.set_xlim(0, 65) 
 
    
# Setting labels for x-axis and y-axis 
gnt.set_xlabel('Batch Completion Time') 
gnt.set_ylabel('Solution by') 
  
# Setting ticks on y-axis 
gnt.set_yticks([10, 25])
gnt.set_yticklabels(['Algo_1', 'Algo_2']) 

# Setting graph attribute 
gnt.grid(True) 

     
#For Algo-1 Solution
gnt.broken_barh([(5,9), (14,1) , (15,4) , (19,9) , (28,4) , (34,4) , (38,5)],(5, 10),\
                facecolors = {'tab:blue','tab:red', 'tab:olive', 'tab:pink', 'tab:cyan', 'tab:brown', 'tab:orange'})
    

#For Algo-2 Solution
gnt.broken_barh([(14,6), (22,4) , (29,7) , (36,3) , (39,15)],(20,10),\
                facecolors = {'tab:blue','tab:red', 'tab:olive', 'tab:pink', 'tab:cyan'})
    
#upto here Gantt Chart is drawn
#Process of showing data while moving the mouse over the chart


annot = gnt.annotate("", xy=(0,0), xytext=(20,30),textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="black", ec="b", lw=2),
                    arrowprops=dict(arrowstyle="->"))

annot.set_visible(False)

def update_annot(bar):
    
    x = bar.get_x() + bar.get_width()
    y = bar.get_y()+  (0.5*bar.get_height())
    
    annot.xy = (x,y) #box no (x,y) cordinate update karse
    text = "{:.2g},{:.2g}".format(x,y)
    annot.set_text(text)
    annot.get_bbox_patch().set_alpha(0.9)  

    
def hover(event):
    vis = annot.get_visible()
    if event.inaxes == gnt:
        for bar in gnt:
            cont, ind = bar.contains(event)
            if cont:
                update_annot(bar)
                annot.set_visible(True)
                fig.canvas.draw_idle()
                return
    if vis:
        annot.set_visible(False)
        fig.canvas.draw_idle()

fig.canvas.mpl_connect("motion_notify_event", hover)

plt.show()

I would like to see (x,y)= (Batch Processing Time, Algorithm Name) value when I move hover the over the batch solution.

python matplotlib annotations
2个回答
3
投票

更新: 代码已经过调整,可以在靠近左侧或右侧时更改框的位置。这样,注释在绘图中保持更好的可见性。 (不幸的是,

mplcursors
不能与
broken_barh
一起使用。)

broken_barh
不会创建单个条形,而是创建一个大
BrokenBarHCollection
对象。 当调用
contains(event)
时,返回
False
True
,以及指示哪个小条被单击的索引。

使用

.get_paths()[ind].get_extents()
可以获得那个小条的边界框。边界框的坐标导致开始时间和持续时间。

import matplotlib.pyplot as plt


def update_annot(brokenbar_collection, coll_id, ind, x, y):
    annot.xy = (x, y)
    box = brokenbar_collection.get_paths()[ind].get_extents()
    text = f"{ax_gnt.get_yticklabels()[coll_id].get_text()} index:{ind} duration:{box.x1 - box.x0:.0f} "
    annot.set_text(text)
    annot.get_bbox_patch().set_alpha(0.8)
    if x > xmax - (xmax - xmin) * 0.3:
        annot.set(position=(-20, 30), anncoords="offset points", horizontalalignment='right')
    elif x < xmin + (xmax - xmin) * 0.3:
        annot.set(position=(20, 30), anncoords="offset points", horizontalalignment='left')


def hover(event):
    vis = annot.get_visible()
    if event.inaxes == ax_gnt:
        for coll_id, brokenbar_collection in enumerate(ax_gnt.collections):
            cont, ind = brokenbar_collection.contains(event)
            if cont:
                update_annot(brokenbar_collection, coll_id, ind['ind'][0], event.xdata, event.ydata)
                annot.set_visible(True)
                fig.canvas.draw_idle()
                return
    if vis:
        annot.set_visible(False)
        fig.canvas.draw_idle()


fig, ax_gnt = plt.subplots()
ax_gnt.set_ylim(0, 45)
xmin, xmax = 0, 65
ax_gnt.set_xlim(xmin, xmax)

ax_gnt.set_yticks([10, 25])
ax_gnt.set_yticklabels(['Algo_1', 'Algo_2'])

ax_gnt.grid(True)

# For Algo-1 Solution
ax_gnt.broken_barh([(5, 9), (14, 1), (15, 4), (19, 9), (28, 4), (34, 4), (38, 5)], (5, 10),
                   facecolors={'tab:blue', 'tab:red', 'tab:olive', 'tab:pink', 'tab:cyan', 'tab:brown', 'tab:orange'})
# For Algo-2 Solution
ax_gnt.broken_barh([(14, 6), (22, 4), (29, 7), (36, 3), (39, 15)], (20, 10),
                   facecolors={'tab:blue', 'tab:red', 'tab:olive', 'tab:pink', 'tab:cyan'})

annot = ax_gnt.annotate("", xy=(0, 0), xytext=(20, 30), textcoords="offset points",
                        bbox=dict(boxstyle="round", fc="yellow", ec="b", lw=2),
                        arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)

fig.canvas.mpl_connect("motion_notify_event", hover)

plt.show()


0
投票

JohanC,你救了我的命……如果使用 pyqt5,需要进行一些小修正

                sc.axes.broken_barh([(h[0][xc]["start"],h[0][xc]["interval"])], (h[0][xc]["id"], 0.5), facecolors =(u))
                annot = sc.axes.annotate(str(h[0][xc]["interval"]), xy=(h[0][xc]["start"],h[0][xc]["id"]), xytext=(20, 30), textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="yellow", ec="b", lw=2),
                    arrowprops=dict(arrowstyle="->"))
                annot.set_visible(False)
                def update_annot(brokenbar_collection, coll_id, ind, x, y):
                    annot.xy = (x, y)
                    box = brokenbar_collection.get_paths()[ind].get_extents()
                    text = f"{sc.axes.get_yticklabels()[coll_id].get_text()} index:{ind} duration:{box.x1 - box.x0:.0f} "
                    annot.set_text(text)
                    annot.get_bbox_patch().set_alpha(0.9)

                def hover(event):
                    vis = annot.get_visible()
                    print("####",sc.axes.collections)
                    print("###",event.inaxes)
                    if event.inaxes == sc.axes:
                        print
                        for coll_id, brokenbar_collection in enumerate(sc.axes.collections):
                            cont, ind = brokenbar_collection.contains(event)
                            if cont:
                                update_annot(brokenbar_collection, coll_id, ind['ind'][0], event.xdata, event.ydata)
                                annot.set_visible(True)
                                sc.figure.canvas.draw_idle()
                                return
                    if vis:
                        annot.set_visible(False)
                        sc.figure.canvas.draw_idle()
                sc.figure.canvas.mpl_connect("motion_notify_event", hover)
© www.soinside.com 2019 - 2024. All rights reserved.