背景
根据前面的博文: python【matplotlib】画图鼠标缩放拖动动态改变坐标轴范围
和Python【Matplotlib】图例可拖动改变位置
两个博文,博主考虑了一下,如何将两者的功能结合起来,让二者共存。
只需根据Python【Matplotlib】鼠标单击事件判断点击的是否为图例
博文所说的,判断鼠标的单击坐标是否在图例所在的区域内,然后进行区分即可
效果
单独鼠标缩放拖动动态改变坐标轴范围-效果
单独拖动图例效果
直接把二者结合起来,会因为Axis占用鼠标事件,而导致拖动图例失效,
进行一些坐标判断,判断鼠标的单击坐标是否在图例所在的区域内,然后进行区分即可完美实现。
if axtemp.get_legend(): legend_bbox = axtemp.get_legend().get_window_extent() left_bottom = legend_bbox.get_points()[0] right_top = legend_bbox.get_points()[1] if left_bottom[0] <= mouse_x <= right_top[0] and left_bottom[1] <= mouse_y <= right_top[1]: # print("在图例上按下鼠标") # 在图例上按下鼠标 mPress = False
直接上代码:
import matplotlib.pyplot as plt from matplotlib.ticker import AutoLocator, MultipleLocator, MaxNLocator plt.rcParams["font.family"] = "Microsoft YaHei" # 创建一个示例图形 fig, ax = plt.subplots() ax.plot([1, 2, 3, ], [2, 4, 6, ], label=f'位移', ls='-') # 使用AutoLocator自动选择刻度位置 # ax.xaxis.set_major_locator(AutoLocator()) ax.yaxis.set_major_locator(AutoLocator()) # 使用MultipleLocator设置x轴刻度间隔为100 # ax.xaxis.set_major_locator(MultipleLocator(200)) # ax.yaxis.set_major_locator(MultipleLocator(100)) # 使用MaxNLocator设置x轴刻度最多显示5个 ax.xaxis.set_major_locator(MaxNLocator(10)) ax.set_xlabel('时间(s)', color='black') ax.set_ylabel('位移(m)', color='black') ax.legend(loc='lower right', # 设置图例位置 labelspacing=0, # 设置图例间距 handlelength=2, # 设置图例中线的长度 ncol=4, # 设置图例的列数 fontsize=8, # 设置图例字体大小 shadow=True, # 设置图例阴影 draggable=True # 设置图例可拖动 ) startx = 0 starty = 0 mPress = False # 鼠标拖动 处理事件 def call_move(event): # print(event.name) global mPress global startx global starty mouse_x = event.x mouse_y = event.y axtemp = event.inaxes if event.name == 'button_press_event': if axtemp and event.button == 1: if axtemp.get_legend(): legend_bbox = axtemp.get_legend().get_window_extent() left_bottom = legend_bbox.get_points()[0] right_top = legend_bbox.get_points()[1] if left_bottom[0] <= mouse_x <= right_top[0] and left_bottom[1] <= mouse_y <= right_top[1]: # print("在图例上按下鼠标") # 在图例上按下鼠标 mPress = False return # 没有图例的情况 # print("在 Axes 上按下鼠标") # 在 Axes 上按下鼠标 mPress = True startx = event.xdata starty = event.ydata return elif event.name == 'button_release_event': if axtemp and event.button == 1: mPress = False elif event.name == 'motion_notify_event': if axtemp and event.button == 1 and mPress: if axtemp.get_legend(): legend_bbox = axtemp.get_legend().get_window_extent() left_bottom = legend_bbox.get_points()[0] right_top = legend_bbox.get_points()[1] if left_bottom[0] <= mouse_x <= right_top[0] and left_bottom[1] <= mouse_y <= right_top[1]: print("在图例上移动鼠标") # 在图例上按下鼠标 mPress = False return # 没有图例的情况 # print("在Axes上移动鼠标") x_min, x_max = axtemp.get_xlim() y_min, y_max = axtemp.get_ylim() w = x_max - x_min h = y_max - y_min # print(event) # 移动 mx = event.xdata - startx my = event.ydata - starty # 注意这里, -mx, 因为下一次 motion事件的坐标,已经是在本次做了移动之后的坐标系了,所以要体现出来 # startx=event.xdata-mx startx=event.xdata-(event.xdata-startx)=startx, 没必要再赋值了 # starty=event.ydata-my # print(mx,my,x_min,y_min,w,h) axtemp.set(xlim=(x_min - mx, x_min - mx + w)) axtemp.set(ylim=(y_min - my, y_min - my + h)) fig.canvas.draw_idle() # 绘图动作实时反映在图像上 return # 滚轮滚动 处理事件 def call_scroll(event): # print(event.name) axtemp = event.inaxes # print('event:', event) # print(event.xdata, event.ydata) # 计算放大缩小后, xlim 和ylim if axtemp: x_min, x_max = axtemp.get_xlim() y_min, y_max = axtemp.get_ylim() w = x_max - x_min h = y_max - y_min curx = event.xdata cury = event.ydata curXposition = (curx - x_min) / w curYposition = (cury - y_min) / h if event.button == 'down': # print('befor:', w, h) w = w * 1.1 h = h * 1.1 # print('down', w, h) elif event.button == 'up': # print('befor:', w, h) w = w / 1.1 h = h / 1.1 # print('up', w, h) # print(curXposition, curYposition) newx = curx - w * curXposition newy = cury - h * curYposition axtemp.set(xlim=(newx, newx + w)) axtemp.set(ylim=(newy, newy + h)) fig.canvas.draw_idle() # 绘图动作实时反映在图像上 fig.canvas.mpl_connect('scroll_event', call_scroll) fig.canvas.mpl_connect('button_press_event', call_move) fig.canvas.mpl_connect('button_release_event', call_move) # fig.canvas.mpl_connect('draw_event', call_move) fig.canvas.mpl_connect('motion_notify_event', call_move) plt.show()