Matplotlib多轴拾取事件

hmtdttj4  于 2023-05-18  发布在  其他
关注(0)|答案(2)|浏览(104)

我正在用两个轴绘制支持拾取线的图。我使用matplotlib和twinx()命令。不幸的是,pick事件仅为最顶部轴中的艺术家调用(参见下面的示例)。

import matplotlib.pyplot as plt
import numpy as np

def onPick(event):
    print(event.artist.get_label())
    
def pick():
    fig=plt.figure(figsize=(5, 4), dpi=100, tight_layout=True)
    axis_1=fig.add_subplot(111)
    axis_2=axis_1.twinx()

    axis_1.set_ylim(0, 10)
    axis_2.set_ylim(0, 10)

    x=np.array([1,2,3,4])
    y_1=np.array([1,1,1,1])
    y_2=y_1+4
    
    axis_1.plot(x, y_1, picker=5, label='line_1')
    axis_2.plot(x, y_2, picker=5, label='line_2')
    
    fig.canvas.mpl_connect('pick_event', onPick)
    plt.show()

if __name__=='__main__':
    pick()

有没有办法从下面的坐标轴中选择直线?

vulvrdjw

vulvrdjw1#

不可能,我找到了解决办法。我不是从斧头上挑出来的,我是从传说中挑出来的。
我认为这是一个很好的妥协。

import matplotlib.pyplot as plt
import numpy as np
from numpy.random import rand

def onpick(event):
        print(event.artist.get_label())

if __name__ == '__main__':
    t=np.linspace(1, 10, 100)
    y1, y2=1*t, 2*t
    y3, y4=3*t, 4*t

    fig, ax1=plt.subplots()
    ax2=ax1.twinx()
    ax2._get_lines.prop_cycler = ax1._get_lines.prop_cycler # Send Color cycle state to second axis.
    
    line1, = ax1.plot(t, y1, lw=2, label='1 HZ')
    line2, = ax1.plot(t, y2, lw=2, label='2 HZ')
    line3, = ax2.plot(t, y3, lw=2, label='3 HZ')
    line4, = ax2.plot(t, y4, lw=2, label='4 HZ')
    
    leg=ax1.legend(handles=[line1, line2, line3, line4], bbox_to_anchor=(0,1.02,1,0.2), loc="lower left", mode="expand", borderaxespad=0, ncol=3)
    for line in leg.get_lines(): line.set_picker(5)
    
    fig.canvas.mpl_connect('pick_event', onpick)
    plt.show()
qnakjoqk

qnakjoqk2#

不是不可能!但是,您必须处理button_press_event,并通过查看每个绘制的line来找到MouseEvent,从而自行确定选择了什么,而不是使用pick_event。每个axes还需要它自己的annotation,否则您将很难找到正确的注解位置。
以下是我在this answer的启发下所做的:

from dataclasses import dataclass
from matplotlib.backend_bases import PickEvent
import matplotlib.pyplot as plt
import matplotlib

def on_button_press(event):
    if not event.inaxes:
        return # off canvas.

    all_picked = [dca for dca in dcAxes if dca.line.contains(event)[0]]
    if not all_picked:
        return # nothing was picked

    picked = all_picked[0] # take the first

    ind = picked.line.contains(event)[1]['ind']
    x_index = ind[0]

    x_val = picked.line.get_xdata()[x_index]
    y_val = picked.line.get_ydata()[x_index]

    annotation_visbility = picked.annotation.get_visible()
    if annotation_visbility and picked.annotation.xy==(x_val,y_val):
        picked.annotation.set_visible(False)
        fig.canvas.draw_idle()
    else:
        picked.annotation.xy = (x_val,y_val)

        text_label = f'{picked.line.get_label()}:({x_val},{y_val})'
        picked.annotation.set_text(text_label)

        picked.annotation.set_visible(True)
        fig.canvas.draw_idle()

 # create data to plot
x = []
y = []
y.append([])
y.append([])
for i in range(10):
    x.append(i)
    y[0].append(i)
    y[1].append(i*i)

# create plots, saving axes/line/annotation for lookup
@dataclass
class DataClassAxes:
    ax: plt.axes
    line: matplotlib.lines.Line2D
    annotation: matplotlib.text.Annotation

dcAxes: list[DataClassAxes] = []

for i in range(2):
    if i==0:
        fig, ax = plt.subplots()
        line, = ax.plot(x, y[i], 'o', picker=5, color='red', label='reds')
    else:
        ax = dcAxes[0].ax.twinx()
        line, = ax.plot(x, y[i], 'o', picker=5, color='blue', label='blues')

    annotation = ax.annotate(
        text='',
        xy=(0, 0),
        xytext=(15, 15), # distance from x, y
        textcoords='offset points',
        bbox={'boxstyle': 'round', 'fc': 'w'},
        arrowprops={'arrowstyle': '->'}
    )
    annotation.set_visible(False)

    dcAxes.append(DataClassAxes(ax, line, annotation))

fig.canvas.mpl_connect('button_press_event', on_button_press)

plt.show()

相关问题