Как прикрепить текстовое поле к курсору мыши в matplotlib? - PullRequest
0 голосов
/ 30 апреля 2019

У меня есть кривая данных, отображаемая на рисунке matplotlib. Я хотел бы прикрепить текстовое поле к курсору мыши. То есть, когда курсор мыши перемещается по фигуре, текстовое поле перемещается вместе с ним. Кроме того, я хотел бы иметь возможность обновлять текст в текстовом поле при перемещении курсора с прикрепленным текстовым полем.

Я начал с примера matplotlib со https://matplotlib.org/gallery/misc/cursor_demo_sgskip.html, но мне не удалось изменить его для моей цели. Я также рассмотрел некоторые сторонние пакеты (например, 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()

Этот код будет перемещать текст с помощью курсора и стирать предыдущий текст. Тем не менее, я все еще не могу изменить текст при перемещении курсора! Любые предложения будут оценены: -)

Ответы [ 2 ]

0 голосов
/ 30 апреля 2019

Вот слегка отредактированная версия кода Дизиета Асахитхата, которая отвечает на мой вопрос:

# -*- 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 normal 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 (this is required!)
        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 along with the text box
        """
        if not event.inaxes:            
            return
        x, y = event.xdata, event.ydata       # x,y coordinates of mouse
        # Update the position of the text in the box attached to the cursor
        self.txt.set_position((x+0.02,y))     # place the text in the box

        # Place the center of the cross-hairs
        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 text in the text box
        self.txt.set_text('Test\n 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.5,7.2))
ax.set_ylim(-1,+1)
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()

Большое спасибо за ваши усилия DA: -)

0 голосов
/ 30 апреля 2019

Вы создавали новый объект 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...