如何用鼠标在Matplotlib/PyQt5中绘制交互式固定m×n网格?
实现交互式m×n固定网格绘制(替代矩形选择)
现有代码可通过鼠标绘制交互式矩形,并能控制其尺寸与位置。请问如何修改代码,实现绘制固定m×n的交互式网格(整体可像矩形一样拖动、调整大小,内部自动划分成m行n列的网格线),而非单一矩形?
原代码
from matplotlib.widgets import RectangleSelector import numpy as np import matplotlib.pyplot as plt def line_select_callback(eclick, erelease): 'eclick and erelease are the press and release events' x1, y1 = eclick.xdata, eclick.ydata x2, y2 = erelease.xdata, erelease.ydata print("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)) print(" The button you used were: %s %s" % (eclick.button, erelease.button)) def toggle_selector(event): print(' Key pressed.') if event.key in ['Q', 'q'] and toggle_selector.RS.active: print(' RectangleSelector deactivated.') toggle_selector.RS.set_active(False) if event.key in ['A', 'a'] and not toggle_selector.RS.active: print(' RectangleSelector activated.') toggle_selector.RS.set_active(True) fig, current_ax = plt.subplots() # make a new plotting range N = 100000 # If N is large one can see x = np.linspace(0.0, 10.0, N) # improvement by use blitting! plt.plot(x, +np.sin(.2*np.pi*x), lw=3.5, c='b', alpha=.7) # plot something plt.plot(x, +np.cos(.2*np.pi*x), lw=3.5, c='r', alpha=.5) plt.plot(x, -np.sin(.2*np.pi*x), lw=3.5, c='g', alpha=.3) print("\n click --> release") # drawtype is 'box' or 'line' or 'none' toggle_selector.RS = RectangleSelector(current_ax, line_select_callback, drawtype='box', useblit=True, button=[1, 3], # don't use middle button minspanx=5, minspany=5, spancoords='pixels', interactive=True) plt.connect('key_press_event', toggle_selector) plt.show()
修改方案
通过继承RectangleSelector自定义网格选择器,保留原交互逻辑的同时添加网格绘制功能:
from matplotlib.widgets import RectangleSelector import numpy as np import matplotlib.pyplot as plt class GridSelector(RectangleSelector): def __init__(self, ax, onselect, m=3, n=3, **kwargs): self.m = m # 网格行数 self.n = n # 网格列数 super().__init__(ax, onselect, **kwargs) def _draw_shape(self, extents): # 先绘制基础矩形框 super()._draw_shape(extents) x1, x2, y1, y2 = extents # 计算垂直网格线(列方向) x_grid = np.linspace(x1, x2, self.n + 1) # 计算水平网格线(行方向) y_grid = np.linspace(y1, y2, self.m + 1) # 绘制垂直网格线 for x in x_grid: self.ax.plot([x, x], [y1, y2], color='gray', linestyle='--', linewidth=1) # 绘制水平网格线 for y in y_grid: self.ax.plot([x1, x2], [y, y], color='gray', linestyle='--', linewidth=1) def line_select_callback(eclick, erelease): 'eclick and erelease are the press and release events' x1, y1 = eclick.xdata, eclick.ydata x2, y2 = erelease.xdata, erelease.ydata print(f"({x1:.2f}, {y1:.2f}) --> ({x2:.2f}, {y2:.2f})") print(f" 使用的按键: {eclick.button} {erelease.button}") def toggle_selector(event): print(' 按键已按下.') if event.key in ['Q', 'q'] and toggle_selector.GS.active: print(' GridSelector 已关闭.') toggle_selector.GS.set_active(False) if event.key in ['A', 'a'] and not toggle_selector.GS.active: print(' GridSelector 已激活.') toggle_selector.GS.set_active(True) fig, current_ax = plt.subplots() N = 100000 x = np.linspace(0.0, 10.0, N) plt.plot(x, +np.sin(.2*np.pi*x), lw=3.5, c='b', alpha=.7) plt.plot(x, +np.cos(.2*np.pi*x), lw=3.5, c='r', alpha=.5) plt.plot(x, -np.sin(.2*np.pi*x), lw=3.5, c='g', alpha=.3) print("\n 点击 --> 释放") # 初始化GridSelector,可自定义m和n调整网格行列数 toggle_selector.GS = GridSelector(current_ax, line_select_callback, m=3, n=3, drawtype='box', useblit=False, button=[1, 3], minspanx=5, minspany=5, spancoords='pixels', interactive=True) plt.connect('key_press_event', toggle_selector) plt.show()
关键说明
- 自定义
GridSelector类,新增m(行数)和n(列数)参数,按需调整网格密度 - 重写
_draw_shape方法,在矩形框基础上绘制灰色虚线网格,网格会跟随矩形的拖动、缩放实时更新 - 关闭
useblit参数(若保留blitting,需自定义缓存更新逻辑,否则网格线无法实时刷新) - 保留原有的快捷键操作:按
A激活网格选择器,按Q关闭
内容的提问来源于stack exchange,提问作者Raziel Itzhak
相关产品推荐
相关产品推荐

