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
投稿

猜你喜欢

  • Python字符串本身作为bytes进行解码的问题

    2022-12-22 07:18:42
  • Python实现Restful API的例子

    2023-03-23 18:28:10
  • python DataFrame 取差集实例

    2021-06-06 01:22:24
  • python分布式计算dispy的使用详解

    2021-01-14 09:15:24
  • [翻译]标记语言和样式手册 Chapter 11 打印样式

    2008-02-11 18:44:00
  • Python + Requests + Unittest接口自动化测试实例分析

    2021-08-08 05:41:47
  • 多种网页弹出窗口代码

    2007-11-24 21:13:00
  • 关联的 script 标签

    2009-11-02 10:40:00
  • PHP入门基础之注释的写法

    2023-10-20 07:26:17
  • Python如何将函数值赋给变量

    2022-01-01 22:58:22
  • 小白教你PyCharm从下载到安装再到科学使用PyCharm2020最新激活码

    2021-03-25 05:57:35
  • 简单了解python中的与或非运算

    2021-03-20 01:45:02
  • 使用python的pexpect模块,实现远程免密登录的示例

    2022-10-19 18:30:22
  • Python脚本利用adb进行手机控制的方法

    2023-01-31 20:49:47
  • 如何在Python项目中引入日志

    2023-01-25 15:10:53
  • python GUI库图形界面开发之PyQt5结合Qt Designer创建信号与槽的详细方法与实例

    2021-09-24 03:11:51
  • 对json字符串与python字符串的不同之处详解

    2023-11-22 04:35:39
  • python实现批量转换文件编码(批转换编码示例)

    2023-07-28 22:03:47
  • python opencv实现图像矫正功能

    2022-05-22 17:00:13
  • Opera Mini 5 网站开发速记

    2010-04-20 16:29:00
  • asp之家 网络编程 m.aspxhome.com