matplotlib绘制鼠标的十字光标的实现(自定义方式,官方实例)

作者:mighty13 时间:2021-06-09 02:33:06 

matplotlib在widgets模块提供Cursor类用于支持十字光标的生成。另外官方还提供了自定义十字光标的实例。

widgets模块Cursor类源码


class Cursor(AxesWidget):
 """
 A crosshair cursor that spans the axes and moves with mouse cursor.

For the cursor to remain responsive you must keep a reference to it.

Parameters
 ----------
 ax : `matplotlib.axes.Axes`
   The `~.axes.Axes` to attach the cursor to.
 horizOn : bool, default: True
   Whether to draw the horizontal line.
 vertOn : bool, default: True
   Whether to draw the vertical line.
 useblit : bool, default: False
   Use blitting for faster drawing if supported by the backend.

Other Parameters
 ----------------
 **lineprops
   `.Line2D` properties that control the appearance of the lines.
   See also `~.Axes.axhline`.

Examples
 --------
 See :doc:`/gallery/widgets/cursor`.
 """

def __init__(self, ax, horizOn=True, vertOn=True, useblit=False,
        **lineprops):
   AxesWidget.__init__(self, ax)

self.connect_event('motion_notify_event', self.onmove)
   self.connect_event('draw_event', self.clear)

self.visible = True
   self.horizOn = horizOn
   self.vertOn = vertOn
   self.useblit = useblit and self.canvas.supports_blit

if self.useblit:
     lineprops['animated'] = True
   self.lineh = ax.axhline(ax.get_ybound()[0], visible=False, **lineprops)
   self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **lineprops)

self.background = None
   self.needclear = False

def clear(self, event):
   """Internal event handler to clear the cursor."""
   if self.ignore(event):
     return
   if self.useblit:
     self.background = self.canvas.copy_from_bbox(self.ax.bbox)
   self.linev.set_visible(False)
   self.lineh.set_visible(False)

def onmove(self, event):
   """Internal event handler to draw the cursor when the mouse moves."""
   if self.ignore(event):
     return
   if not self.canvas.widgetlock.available(self):
     return
   if event.inaxes != self.ax:
     self.linev.set_visible(False)
     self.lineh.set_visible(False)

if self.needclear:
       self.canvas.draw()
       self.needclear = False
     return
   self.needclear = True
   if not self.visible:
     return
   self.linev.set_xdata((event.xdata, event.xdata))

self.lineh.set_ydata((event.ydata, event.ydata))
   self.linev.set_visible(self.visible and self.vertOn)
   self.lineh.set_visible(self.visible and self.horizOn)

self._update()

def _update(self):
   if self.useblit:
     if self.background is not None:
       self.canvas.restore_region(self.background)
     self.ax.draw_artist(self.linev)
     self.ax.draw_artist(self.lineh)
     self.canvas.blit(self.ax.bbox)
   else:
     self.canvas.draw_idle()
   return False

自定义十字光标实现

简易十字光标实现

首先在 Cursor类的构造方法__init__中,构造了十字光标的横线、竖线和坐标显示;然后在on_mouse_move方法中,根据事件数据更新横竖线和坐标显示,最后在调用时,通过mpl_connect方法绑定on_mouse_move方法和鼠标移动事件'motion_notify_event'。


import matplotlib.pyplot as plt
import numpy as np

class Cursor:
 """
 A cross hair cursor.
 """
 def __init__(self, ax):
   self.ax = ax
   self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
   self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
   # text location in axes coordinates
   self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)

def set_cross_hair_visible(self, visible):
   need_redraw = self.horizontal_line.get_visible() != visible
   self.horizontal_line.set_visible(visible)
   self.vertical_line.set_visible(visible)
   self.text.set_visible(visible)
   return need_redraw

def on_mouse_move(self, event):
   if not event.inaxes:
     need_redraw = self.set_cross_hair_visible(False)
     if need_redraw:
       self.ax.figure.canvas.draw()
   else:
     self.set_cross_hair_visible(True)
     x, y = event.xdata, event.ydata
     # update the line positions
     self.horizontal_line.set_ydata(y)
     self.vertical_line.set_xdata(x)
     self.text.set_text('x=%1.2f, y=%1.2f' % (x, y))
     self.ax.figure.canvas.draw()

x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)

fig, ax = plt.subplots()
ax.set_title('Simple cursor')
ax.plot(x, y, 'o')
cursor = Cursor(ax)
#关键部分,绑定鼠标移动事件处理
fig.canvas.mpl_connect('motion_notify_event', cursor.on_mouse_move)
plt.show()

优化十字光标实现

在简易实现中,每次鼠标移动时,都会重绘整个图像,这样效率比较低。
在优化实现中,每次鼠标移动时,只重绘光标和坐标显示,背景图像不再重绘。


import matplotlib.pyplot as plt
import numpy as np

class BlittedCursor:
 """
 A cross hair cursor using blitting for faster redraw.
 """
 def __init__(self, ax):
   self.ax = ax
   self.background = None
   self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
   self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
   # text location in axes coordinates
   self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
   self._creating_background = False
   ax.figure.canvas.mpl_connect('draw_event', self.on_draw)

def on_draw(self, event):
   self.create_new_background()

def set_cross_hair_visible(self, visible):
   need_redraw = self.horizontal_line.get_visible() != visible
   self.horizontal_line.set_visible(visible)
   self.vertical_line.set_visible(visible)
   self.text.set_visible(visible)
   return need_redraw

def create_new_background(self):
   if self._creating_background:
     # discard calls triggered from within this function
     return
   self._creating_background = True
   self.set_cross_hair_visible(False)
   self.ax.figure.canvas.draw()
   self.background = self.ax.figure.canvas.copy_from_bbox(self.ax.bbox)
   self.set_cross_hair_visible(True)
   self._creating_background = False

def on_mouse_move(self, event):
   if self.background is None:
     self.create_new_background()
   if not event.inaxes:
     need_redraw = self.set_cross_hair_visible(False)
     if need_redraw:
       self.ax.figure.canvas.restore_region(self.background)
       self.ax.figure.canvas.blit(self.ax.bbox)
   else:
     self.set_cross_hair_visible(True)
     # update the line positions
     x, y = event.xdata, event.ydata
     self.horizontal_line.set_ydata(y)
     self.vertical_line.set_xdata(x)
     self.text.set_text('x=%1.2f, y=%1.2f' % (x, y))

self.ax.figure.canvas.restore_region(self.background)
     self.ax.draw_artist(self.horizontal_line)
     self.ax.draw_artist(self.vertical_line)
     self.ax.draw_artist(self.text)
     self.ax.figure.canvas.blit(self.ax.bbox)

x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)

fig, ax = plt.subplots()
ax.set_title('Blitted cursor')
ax.plot(x, y, 'o')
blitted_cursor = BlittedCursor(ax)
fig.canvas.mpl_connect('motion_notify_event', blitted_cursor.on_mouse_move)
plt.show()

捕捉数据十字光标实现

在前面的两种实现中,鼠标十字光标可以随意移动。在本实现中,十字光标只会出现在离鼠标x坐标最近的数据点上。


import matplotlib.pyplot as plt
import numpy as np

class SnappingCursor:
 """
 A cross hair cursor that snaps to the data point of a line, which is
 closest to the *x* position of the cursor.

For simplicity, this assumes that *x* values of the data are sorted.
 """
 def __init__(self, ax, line):
   self.ax = ax
   self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
   self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
   self.x, self.y = line.get_data()
   self._last_index = None
   # text location in axes coords
   self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)

def set_cross_hair_visible(self, visible):
   need_redraw = self.horizontal_line.get_visible() != visible
   self.horizontal_line.set_visible(visible)
   self.vertical_line.set_visible(visible)
   self.text.set_visible(visible)
   return need_redraw

def on_mouse_move(self, event):
   if not event.inaxes:
     self._last_index = None
     need_redraw = self.set_cross_hair_visible(False)
     if need_redraw:
       self.ax.figure.canvas.draw()
   else:
     self.set_cross_hair_visible(True)
     x, y = event.xdata, event.ydata
     index = min(np.searchsorted(self.x, x), len(self.x) - 1)
     if index == self._last_index:
       return # still on the same data point. Nothing to do.
     self._last_index = index
     x = self.x[index]
     y = self.y[index]
     # update the line positions
     self.horizontal_line.set_ydata(y)
     self.vertical_line.set_xdata(x)
     self.text.set_text('x=%1.2f, y=%1.2f' % (x, y))
     self.ax.figure.canvas.draw()

x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)

fig, ax = plt.subplots()
ax.set_title('Snapping cursor')
line, = ax.plot(x, y, 'o')
snap_cursor = SnappingCursor(ax, line)
fig.canvas.mpl_connect('motion_notify_event', snap_cursor.on_mouse_move)
plt.show()

参考资料

https://www.matplotlib.org.cn/gallery/misc/cursor_demo_sgskip.html

来源:https://blog.csdn.net/mighty13/article/details/112346707

标签:matplotlib,鼠标,光标
0
投稿

猜你喜欢

  • 为什么mysql自增主键不是连续的

    2024-01-19 06:59:53
  • pytorch geometric的GNN、GCN的节点分类方式

    2022-12-24 16:01:23
  • 使用Python实现批量ping操作方法

    2021-04-06 02:55:34
  • 推荐个辅助软件FastStone Capture

    2008-12-23 13:23:00
  • 用户"sa"登陆失败 SQLServer 错误18456的解决方法

    2024-01-18 18:04:37
  • 使用Go module和GoLand初始化一个Go项目的方法

    2024-05-22 10:13:30
  • Go语言k8s kubernetes使用leader election实现选举

    2024-04-26 17:20:53
  • JDBC连接mysql处理中文时乱码解决办法详解

    2024-01-17 21:35:35
  • asp.net中不能在DropDownList中选择多个项 原因分析及解决方法

    2023-07-23 22:15:27
  • Golang二维切片初始化的实现

    2024-05-09 14:57:54
  • 利用python写个下载teahour音频的小脚本

    2021-05-17 06:05:54
  • JavaScript实现Fly Bird小游戏

    2024-04-25 13:11:46
  • js控制图片闪烁代码

    2008-07-30 12:35:00
  • 详解bootstrap导航栏.nav与.navbar区别

    2023-08-15 19:18:42
  • python3.7简单的爬虫实例详解

    2023-06-30 15:55:13
  • Python虚拟机栈帧对象及获取源码学习

    2022-08-26 07:15:30
  • 解决Python对齐文本字符串问题

    2021-08-20 10:22:26
  • 在pycharm中python切换解释器失败的解决方法

    2021-02-01 20:53:58
  • python 中文编码乱码问题的解决

    2021-03-06 16:43:58
  • Python实现PS图像抽象画风效果的方法

    2022-10-25 14:08:42
  • asp之家 网络编程 m.aspxhome.com