首页 > 解决方案 > 如何在 matplotlib 中将文本框附加到鼠标光标?

问题描述

我在 matplotlib 图中显示了一条数据曲线。我想在鼠标光标上附加一个文本框。也就是说,随着鼠标光标在图中四处移动,文本框也随之移动。另外,我希望能够在移动带有附加文本框的光标时更新文本框中的文本。

我从https://matplotlib.org/gallery/misc/cursor_demo_sgskip.html上的 matplotlib 示例开始,但出于我的目的对其进行了修改失败。我还查看了一些第 3 方包(例如 mpldatacursor 和 mplcursors);但是,这些似乎不适合我的应用程序。

这是我正在尝试的一些代码,应该可以说明我要完成的工作。

# -*- coding: iso-8859-1 -*-#
#!/usr/bin/env python
# The following allows special characters to be in comments (e.g. the extended Swedish alphabet)
# coding:utf-8
import matplotlib.pyplot as plt
# For setting size and position of matplotlib figure 
import matplotlib
matplotlib.use("WXAgg")
import numpy as np

class Cursor(object):
    """
     Purpose: Define a cursor whose interesection will track points along a curve
    """
    def __init__(self, ax):
        self.ax = ax
        self.lx = ax.axhline(color='k',linewidth=0.25)  # the horiz line
        self.ly = ax.axvline(color='k',linewidth=0.25)  # the vert line

        # Text location in axes coords
        self.txt = ax.text(0.7, 0.9, '', transform=ax.transAxes)

    def mouse_move(self, event):
        '''
         Purpose: respond to movement of the mouse
        '''
        if not event.inaxes: return
        x, y = event.xdata, event.ydata
        props = dict(boxstyle='round', facecolor='wheat', alpha=0.4)
        self.ax.text(x, y, 'test',  fontsize=8, bbox=props) 
        #self.ax.text(x,y,'')   
        #self.text(x, y, 'test',  fontsize=8, bbox=props)   
        #ax.text(x, y, 'test',  fontsize=8, bbox=props)   
        # Update the line positions
        self.lx.set_ydata(y)
        self.ly.set_xdata(x)

        self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
        self.ax.text(x,y,'')   
        plt.draw()

class SnaptoCursor(object):
    """
    Like Cursor but the current center of the crosshair at (x,y) will snap to the nearest
    (x,y) on the curve.
    For simplicity, I'm assuming x is sorted
    """
    def __init__(self, ax, x, y):
        self.ax  = ax
        self.lx  = ax.axhline(color='k')  # the horiz line
        self.ly  = ax.axvline(color='k')  # the vert line
        self.x   = x
        self.y   = y
        # Text location in axes coords

        self.txt = ax.text(0.7, 0.9, '', transform=ax.transAxes)


    def mouse_move(self, event):
        """
         Purpose: Track the movement of the mouse coords and then update the position
                  of the intersection of the cursor cross-hairs
        """
        if not event.inaxes:            
            return
        x, y = event.xdata, event.ydata       # x,y coordinates of mouse

        props = dict(boxstyle='round', facecolor='wheat', alpha=0.4)
        self.ax.text(x, y, 'test',  fontsize=8, bbox=props)            
        #self.text(x, y, 'test',  fontsize=8, bbox=props)   
        #ax.text(x, y, 'test',  fontsize=8, bbox=props)           
        #self.ax.text(remove)   

        # Find closest pt on data curve to (x,y) of cross-air intersection
        indx = min(np.searchsorted(self.x, [x])[0], len(self.x) - 1)
        x = self.x[indx]
        y = self.y[indx]
        # Update the line positions
        self.lx.set_ydata(y)
        self.ly.set_xdata(x)
        # place a text box in upper left in axes coords
        #self.ax.text(x, y, 'test', transform=ax.transAxes, fontsize=8,
        #        verticalalignment='top', bbox=props)        

        self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
        print('x=%1.2f, y=%1.2f' % (x, y))
        plt.draw()

t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2 * 2 * np.pi * t)
fig, ax = plt.subplots(figsize=(14,7.5))
fig.canvas.set_window_title('TS with tracking cursor')
# Need the following to set position the plot
pltManager = plt.get_current_fig_manager()
pltManager.window.SetPosition((20,20))  # pixels offset from top left corner of display   

# cursor = Cursor(ax)
cursor = SnaptoCursor(ax, t, s)
plt.connect('motion_notify_event', cursor.mouse_move)

ax.plot (t, s, 'o')   

plt.axis([0, 1, -1, 1])
plt.grid(axis='both')
plt.show()

文本框确实“粘”在鼠标光标上,但在移动光标时不会被删除——这是需要解决的问题。朝着正确方向迈出的一小步

# -*- coding: iso-8859-1 -*-#
#!/usr/bin/env python
# The following allows special characters to be in comments (e.g. the extended Swedish alphabet)
# coding:utf-8
import matplotlib.pyplot as plt
# For setting size and position of matplotlib figure 
import matplotlib
matplotlib.use("WXAgg")
import numpy as np


class SnaptoCursor(object):
    """
    Like Cursor but the current center of the crosshair at (x,y) will snap to the nearest
    (x,y) on the curve.
    For simplicity, I'm assuming x is sorted
    """
    def __init__(self, ax, x, y):
        self.ax  = ax
        self.lx  = ax.axhline(color='k')  # the horiz line
        self.ly  = ax.axvline(color='k')  # the vert line

        self.tx  = ax.text(0.0,0.0,'test')        # the text to follow cursor

        self.x   = x
        self.y   = y
        # Text location in axes coords
        self.txt = ax.text(0.7, 0.9, '', transform=ax.transAxes)


    def mouse_move(self, event):
        """
         Purpose: Track the movement of the mouse coords and then update the position
                  of the intersection of the cursor cross-hairs
        """
        if not event.inaxes:            
            return
        x, y = event.xdata, event.ydata       # x,y coordinates of mouse
        self.tx.set_position((x,y))

        # Find closest pt on data curve to (x,y) of cross-air intersection
        indx = min(np.searchsorted(self.x, [x])[0], len(self.x) - 1)
        x = self.x[indx]
        y = self.y[indx]
        # Update the line positions
        self.lx.set_ydata(y)
        self.ly.set_xdata(x)

        self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
        print('x=%1.2f, y=%1.2f' % (x, y))
        plt.draw()

t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2 * 2 * np.pi * t)
fig, ax = plt.subplots(figsize=(14,7.5))
fig.canvas.set_window_title('TS with tracking cursor')
# Need the following to set position the plot
pltManager = plt.get_current_fig_manager()
pltManager.window.SetPosition((20,20))  # pixels offset from top left corner of display   

cursor = SnaptoCursor(ax, t, s)
plt.connect('motion_notify_event', cursor.mouse_move)

ax.plot (t, s, 'o')   

plt.axis([0, 1, -1, 1])
plt.grid(axis='both')
plt.show()

此代码将使用光标移动文本并删除以前的文本。但是,当光标移动时,我仍然无法更改文本!任何建议,将不胜感激 :-)

标签: pythonmatplotlibanimation

解决方案


Text每次移动鼠标时,您都在创建一个新对象。您需要在期间创建对象__init__,然后在移动鼠标时简单地更新其位置/文本:

# -*- coding: iso-8859-1 -*-#
#!/usr/bin/env python
# The following allows special characters to be in comments (e.g. the extended Swedish alphabet)
# coding:utf-8
import matplotlib.pyplot as plt
# For setting size and position of matplotlib figure 
import matplotlib
matplotlib.use("WXAgg")
import numpy as np

class Cursor(object):
    """
     Purpose: Define a cursor whose interesection will track points along a curve
    """
    def __init__(self, ax):
        self.ax = ax
        self.lx = ax.axhline(color='k',linewidth=0.25)  # the horiz line
        self.ly = ax.axvline(color='k',linewidth=0.25)  # the vert line

        # Text location in data coords
        props = dict(boxstyle='round', facecolor='wheat', alpha=0.4)
        self.txt = self.ax.text(0, 0, '', fontsize=8, bbox=props)

    def mouse_move(self, event):
        '''
         Purpose: respond to movement of the mouse
        '''
        if not event.inaxes: return
        x, y = event.xdata, event.ydata
        self.txt.set_position((x,y))
        # Update the line positions
        self.lx.set_ydata(y)
        self.ly.set_xdata(x)

        self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
        plt.draw()

class SnaptoCursor(object):
    """
    Like Cursor but the current center of the crosshair at (x,y) will snap to the nearest
    (x,y) on the curve.
    For simplicity, I'm assuming x is sorted
    """
    def __init__(self, ax, x, y):
        self.ax  = ax
        self.lx  = ax.axhline(color='k')  # the horiz line
        self.ly  = ax.axvline(color='k')  # the vert line
        self.x   = x
        self.y   = y

        # Text location in data coords
        props = dict(boxstyle='round', facecolor='wheat', alpha=0.4)
        self.txt = self.ax.text(0, 0, '', fontsize=8, bbox=props)


    def mouse_move(self, event):
        """
         Purpose: Track the movement of the mouse coords and then update the position
                  of the intersection of the cursor cross-hairs
        """
        if not event.inaxes:            
            return
        x, y = event.xdata, event.ydata       # x,y coordinates of mouse

        self.txt.set_position((x,y))

        # Find closest pt on data curve to (x,y) of cross-air intersection
        indx = min(np.searchsorted(self.x, [x])[0], len(self.x) - 1)
        x = self.x[indx]
        y = self.y[indx]
        # Update the line positions
        self.lx.set_ydata(y)
        self.ly.set_xdata(x)
        # place a text box in upper left in axes coords
        #self.ax.text(x, y, 'test', transform=ax.transAxes, fontsize=8,
        #        verticalalignment='top', bbox=props)        

        self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
        print('x=%1.2f, y=%1.2f' % (x, y))
        self.ax.figure.canvas.draw_idle()

t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2 * 2 * np.pi * t)
fig, ax = plt.subplots(figsize=(14,7.5))
fig.canvas.set_window_title('TS with tracking cursor')

# cursor = Cursor(ax)
cursor = SnaptoCursor(ax, t, s)
plt.connect('motion_notify_event', cursor.mouse_move)

ax.plot (t, s, 'o')   

plt.axis([0, 1, -1, 1])
plt.grid(axis='both')
plt.show()

推荐阅读