Bug summary
QuadMesh.get_cursor_data() returns the incorrect value, but only for some figure sizes, when an event is near the boundary between two cells and the axis is categorical. By 'incorrect', I mean it doesn't align with the event's xdata and ydata values.
In the below sample I get the cell value by two means and compare them:
- by rounding the
xdata and ydata values from the event to index into the underlying data
- using
get_cursor_data.
They almost always match, but not always.
Code for reproduction
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["backend"] = "TKAgg"
plt.ion()
fig, ax = plt.subplots()
data = np.arange(6).reshape(2, 3)
qm = ax.pcolormesh(
["col 1", "col 2", "col 3"],
["row 1", "row 2"],
data,
)
def on_mouse_move(event):
if not event.inaxes:
return
cd = qm.get_cursor_data(event)
if cd is None:
return
# The value of the cell, according to get_cursor_data()
cd_val = cd[0]
# The value of the cell, according to the data row/col
data_val = data[round(event.ydata), round(event.xdata)]
if cd_val == data_val:
print("Fine")
else:
print(event)
print(cd)
raise ValueError(f"Value mismatch. {cd_val} != {data_val}")
fig.canvas.mpl_connect("motion_notify_event", on_mouse_move)
fig.canvas.manager.window.geometry("800x1054+100+100")
Actual outcome
As you hover the cursor right in the middle where two cells touch, it raises the error when the values don't match.

Expected outcome
The two values should always match.
Additional information
Trying to track down the issue, I noticed that it only happens at some figure heights. When I changed from my default to pretty much anything else, it was difficult to get the error.
So I can confirm that it isn't caused by the call to fig.canvas.manager.window.geometry, but if you don't do this you likely won't be able to replicate the issue.
The real world situation where I discovered this: I was producing a tooltip for a heatmap. I got the y label (Country) using ax.format_ydata(event.ydata) and the cell value using quad_mesh.get_cursor_data(event). I only noticed when the cells are quite small, it gives me incorrect information a lot, e.g. Canada has no bananas, so should show nan (short for no nanas), but it shows the value from Cape Verde instead.

Zoomed in

You can also replicate this without interaction by just firing a bunch of mouse events near the border, this will fail regardless of the figure size.
fig.canvas.draw()
for ax_y in np.linspace(0.49, 0.51, 100):
x, y = ax.transAxes.transform((0.5, ax_y))
mouse_event = MouseEvent(
name="motion_notify_event",
canvas=fig.canvas,
x=x,
y=y,
)
on_mouse_move(mouse_event)
I'm not sure what the fix would look like, it seems to go quite deep. A good interim step would be a note in the docs for get_cursor_data() warning that it isn't guaranteed to align with xdata and ydata near the borders.
Operating system
Windows
Matplotlib Version
3.8.0
Matplotlib Backend
TKAgg
Python version
3.10
Jupyter version
No response
Installation
pip
Bug summary
QuadMesh.get_cursor_data()returns the incorrect value, but only for some figure sizes, when an event is near the boundary between two cells and the axis is categorical. By 'incorrect', I mean it doesn't align with the event'sxdataandydatavalues.In the below sample I get the cell value by two means and compare them:
xdataandydatavalues from the event to index into the underlying dataget_cursor_data.They almost always match, but not always.
Code for reproduction
Actual outcome
As you hover the cursor right in the middle where two cells touch, it raises the error when the values don't match.

Expected outcome
The two values should always match.
Additional information
Trying to track down the issue, I noticed that it only happens at some figure heights. When I changed from my default to pretty much anything else, it was difficult to get the error.
So I can confirm that it isn't caused by the call to
fig.canvas.manager.window.geometry, but if you don't do this you likely won't be able to replicate the issue.The real world situation where I discovered this: I was producing a tooltip for a heatmap. I got the y label (Country) using
ax.format_ydata(event.ydata)and the cell value usingquad_mesh.get_cursor_data(event). I only noticed when the cells are quite small, it gives me incorrect information a lot, e.g. Canada has no bananas, so should shownan(short for no nanas), but it shows the value from Cape Verde instead.Zoomed in

You can also replicate this without interaction by just firing a bunch of mouse events near the border, this will fail regardless of the figure size.
I'm not sure what the fix would look like, it seems to go quite deep. A good interim step would be a note in the docs for
get_cursor_data()warning that it isn't guaranteed to align withxdataandydatanear the borders.Operating system
Windows
Matplotlib Version
3.8.0
Matplotlib Backend
TKAgg
Python version
3.10
Jupyter version
No response
Installation
pip