import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backend_bases import MouseButton as mevent
from matplotlib.backend_bases import Event
from matplotlib.font_manager import FontProperties
class Cursor:
"""
A cross hair cursor.
"""
def __init__(self, ax, horzdata,verticaldata,ax2):
self.ax = ax
self.ax2 = ax2
self.verticaldata = verticaldata
self.horzdata = horzdata
self.x1 = 0
self.y1 = 0
self.x2 = 0
self.y2 = 0
self.actCuror = 'D'
self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
self.horizontal_line_2 = ax.axhline(color='b', lw=1, ls='solid')
self.vertical_line_2 = ax.axvline(color='b', lw=1, ls='solid')
# text location in axes coordinates
self.text_1 = ax2.text (0.05, 0.95,'P0(', transform=ax2.transAxes)
self.text_2 = ax2.text (0.05, 0.9, 'P1(', transform=ax2.transAxes)
self.Text_3 = ax2.text (0.05, 0.85,'dP(', transform=ax2.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.horizontal_line_2.set_visible(visible)
self.vertical_line_2.set_visible(visible)
self.text_1.set_visible(visible)
self.text_2.set_visible(visible)
self.Text_3.set_visible(visible)
return need_redraw
def on_mouse_click(self,event):
#print(event)
if event.name == 'button_press_event':
if event.button == mevent.LEFT:
self.actCuror = "A"
self.horizontal_line.set_ydata(event.ydata)
self.vertical_line.set_xdata(event.xdata)
elif event.button == mevent.RIGHT:
self.actCuror = 'B'
self.horizontal_line_2.set_ydata(event.ydata)
self.vertical_line_2.set_xdata(event.xdata)
else:
self.actCuror = 'D'
print("no move")
self.ax.figure.canvas.draw()
def on_mouse_move(self, event):
#print(event.x)
#print(event.y)
#print(event.xdata)
#print(event.ydata)
if not event.inaxes:
pass
#need_redraw = self.set_cross_hair_visible(False)
#if need_redraw:
# self.ax.figure.canvas.draw()
else:
#print(self.__class__.__name__)
self.set_cross_hair_visible(True)
x = event.xdata
xindx = np.where(self.horzdata >= x)
#print(xindx[0][0])
#y = event.ydata
y = self.verticaldata[xindx[0][0]]
# update the line positions
if self.actCuror == "A":
self.x1 = x
self.y1 = y
self.horizontal_line.set_ydata(y)
self.vertical_line.set_xdata(x)
self.text_1.set_text('P0 = (%1.2f, %1.2f)' % (x, y))
elif self.actCuror == 'B':
self.x2 = x
self.y2 = y
self.horizontal_line_2.set_ydata(y)
self.vertical_line_2.set_xdata(x)
self.text_2.set_text('P1 = (%1.2f, %1.2f)' % (x, y))
self.Text_3.set_text('dP = (%1.2f, %1.2f)' % (abs(self.x1-
self.x2), abs(self.y2-self.y1)))
self.ax.figure.canvas.draw()
x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)
fig.set_constrained_layout_pads( hspace=0, wspace=0)
ax[0].set_title('Simple cursor')
ax[0].plot(x, y, 'o')
ax[0].set_position([0.05, 0.05, 0.75, 0.9])
ax[1].set_position([0.82, 0.05, 0.15, 0.9])
ax[1].set_xticks([])
ax[1].set_yticks([])
#plt.subplots_adjust()
font0 = FontProperties()
alignment = {'horizontalalignment': 'center', 'verticalalignment':\
baseline'}
# Show family options
families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace']
plt.figtext(0.8, 0.5, 'bold italic', fontproperties=font0, **alignment)
cursor = Cursor(ax[0],x,y, ax[1])
fig.canvas.mpl_connect('motion_notify_event', cursor.on_mouse_move)
fig.canvas.mpl_connect('button_press_event', cursor.on_mouse_click)
fig.canvas.mpl_connect('button_release_event', cursor.on_mouse_click)
plt.show()
print("done")
##################
"""
just an idea
make two subplots side by side
and use the right subplot as a text area
check below code
I was trying to make two crosshair coursers with measurement
I created two subplots on the same figure with ases ax[0] for drawing and ax[1]
to hold text
as per my understanding the figure area size is treated as 1x1
I used set_position to adjust the axis size and location
for example ax[0].set_position([0.05, 0.05, 0.75, 0.9])
with set the drawing axis at points x =.05 , y = .05, width = 0.75 & height = 0.9, remeber complet figure is 1x1
also ax[1] for text is positioned according to ax[1].set_position([0.82, 0.05, 0.15, 0.9])
I removed the axis ticks to appear as a box
below lines
self.text_1 = ax2.text (0.05, 0.95,'P0(', transform=ax2.transAxes)
self.text_2 = ax2.text (0.05, 0.9, 'P1(', transform=ax2.transAxes)
self.Text_3 = ax2.text (0.05, 0.85,'dP(', transform=ax2.transAxes)
will show text in the ax[1] area , here dimension are relative to ax[1] area not full figure area
please check below code
on the drawing axis press and hold right/left mouse key and watch the measurement on the ax[1] area
- remember to place a break point at the last line and run in debug mode so
u can play with the code
"""