Skip to content

Commit c624aec

Browse files
QuLogicmeeseeksmachine
authored andcommitted
Backport PR matplotlib#32038: Fix occasional misalignment in reported mouse position (also fixes a bug with canvas height)
1 parent 9001323 commit c624aec

13 files changed

Lines changed: 40 additions & 19 deletions

lib/matplotlib/backend_bases.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2059,8 +2059,16 @@ def get_width_height(self, *, physical=False):
20592059
width, height : int
20602060
The size of the figure, in points or pixels, depending on the
20612061
backend.
2062+
2063+
Notes
2064+
-----
2065+
This method normally truncates the height/width to remove any fractional pixel.
2066+
However, if the height/width is extremely close to the integer pixel (within
2067+
1e-8 pixel), the height/width is instead rounded up to account for
2068+
floating-point precision effects.
20622069
"""
2063-
return tuple(int(size / (1 if physical else self.device_pixel_ratio))
2070+
# The tolerance of 1e-8 covers a floating-point tick for even 100,000 pixels
2071+
return tuple(int(size / (1 if physical else self.device_pixel_ratio) + 1e-8)
20642072
for size in self.figure.bbox.max)
20652073

20662074
@classmethod

lib/matplotlib/backends/_backend_gtk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def set_message(self, s):
275275
self.message.set_markup(f'<small>{escaped}</small>')
276276

277277
def draw_rubberband(self, event, x0, y0, x1, y1):
278-
height = self.canvas.figure.bbox.height
278+
height = self.canvas.get_width_height(physical=True)[1]
279279
y1 = height - y1
280280
y0 = height - y0
281281
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]

lib/matplotlib/backends/_backend_tk.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,10 @@ def get_tk_widget(self):
327327
def _event_mpl_coords(self, event):
328328
# calling canvasx/canvasy allows taking scrollbars into account (i.e.
329329
# the top of the widget may have been scrolled out of view).
330+
height = self.get_width_height(physical=True)[1]
330331
return (self._tkcanvas.canvasx(event.x),
331332
# flipy so y=0 is bottom of canvas
332-
self.figure.bbox.height - self._tkcanvas.canvasy(event.y))
333+
height - self._tkcanvas.canvasy(event.y))
333334

334335
def motion_notify_event(self, event):
335336
MouseEvent("motion_notify_event", self,
@@ -389,7 +390,7 @@ def scroll_event_windows(self, event):
389390
if w != self._tkcanvas:
390391
return
391392
x = self._tkcanvas.canvasx(event.x_root - w.winfo_rootx())
392-
y = (self.figure.bbox.height
393+
y = (self.get_width_height(physical=True)[1]
393394
- self._tkcanvas.canvasy(event.y_root - w.winfo_rooty()))
394395
step = event.delta / 120
395396
MouseEvent("scroll_event", self,
@@ -676,7 +677,7 @@ def __init__(self, canvas, window=None, *, pack_toolbar=True):
676677
if window is None:
677678
window = canvas.get_tk_widget().master
678679
tk.Frame.__init__(self, master=window, borderwidth=2,
679-
width=int(canvas.figure.bbox.width), height=50)
680+
width=canvas.get_width_height()[0], height=50)
680681
# Avoid message_label expanding the toolbar size, and in turn expanding the
681682
# canvas size.
682683
# Without pack_propagate(False), when the user defines a small figure size
@@ -774,7 +775,7 @@ def draw_rubberband(self, event, x0, y0, x1, y1):
774775
self.canvas._tkcanvas.delete(self.canvas._rubberband_rect_white)
775776
if self.canvas._rubberband_rect_black:
776777
self.canvas._tkcanvas.delete(self.canvas._rubberband_rect_black)
777-
height = self.canvas.figure.bbox.height
778+
height = self.canvas.get_width_height(physical=True)[1]
778779
y0 = height - y0
779780
y1 = height - y1
780781
self.canvas._rubberband_rect_black = (

lib/matplotlib/backends/backend_agg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ def draw(self):
441441
super().draw()
442442

443443
def get_renderer(self):
444-
w, h = self.figure.bbox.size
444+
w, h = self.get_width_height(physical=True)
445445
key = w, h, self.figure.dpi
446446
reuse_renderer = (self._lastKey == key)
447447
if not reuse_renderer:

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def _mpl_coords(self, event=None):
119119
x, y = event.x, event.y
120120
x = x * self.device_pixel_ratio
121121
# flip y so y=0 is bottom of canvas
122-
y = self.figure.bbox.height - y * self.device_pixel_ratio
122+
y = self.get_width_height(physical=True)[1] - y * self.device_pixel_ratio
123123
return x, y
124124

125125
def scroll_event(self, widget, event):

lib/matplotlib/backends/backend_gtk4.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def _mpl_coords(self, xy=None):
117117
x, y = xy
118118
x = x * self.device_pixel_ratio
119119
# flip y so y=0 is bottom of canvas
120-
y = self.figure.bbox.height - y * self.device_pixel_ratio
120+
y = self.get_width_height(physical=True)[1] - y * self.device_pixel_ratio
121121
return x, y
122122

123123
def scroll_event(self, controller, dx, dy):

lib/matplotlib/backends/backend_qt.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def mouseEventCoords(self, pos=None):
302302
# (otherwise, it's already a QPoint)
303303
x = pos.x()
304304
# flip y so y=0 is bottom of canvas
305-
y = self.figure.bbox.height / self.device_pixel_ratio - pos.y()
305+
y = self.get_width_height()[1] - pos.y()
306306
return x * self.device_pixel_ratio, y * self.device_pixel_ratio
307307

308308
def enterEvent(self, event):
@@ -915,7 +915,7 @@ def set_message(self, s):
915915
self.locLabel.setText(s)
916916

917917
def draw_rubberband(self, event, x0, y0, x1, y1):
918-
height = self.canvas.figure.bbox.height
918+
height = self.canvas.get_width_height(physical=True)[1]
919919
y1 = height - y1
920920
y0 = height - y0
921921
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]

lib/matplotlib/backends/backend_tkcairo.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99

1010
class FigureCanvasTkCairo(FigureCanvasCairo, FigureCanvasTk):
1111
def draw(self):
12-
width = int(self.figure.bbox.width)
13-
height = int(self.figure.bbox.height)
12+
width, height = self.get_width_height(physical=True)
1413
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
1514
self._renderer.set_context(cairo.Context(surface))
1615
self._renderer.dpi = self.figure.dpi

lib/matplotlib/backends/backend_webagg_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ def add_web_socket(self, web_socket):
492492
assert hasattr(web_socket, 'send_binary')
493493
assert hasattr(web_socket, 'send_json')
494494
self.web_sockets.add(web_socket)
495-
self.resize(*self.canvas.figure.bbox.size)
495+
self.resize(*self.canvas.get_width_height(physical=True))
496496
self._send_event('refresh')
497497

498498
def remove_web_socket(self, web_socket):

lib/matplotlib/backends/backend_wx.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,9 @@ def _mpl_coords(self, pos=None):
746746
# flip y so y=0 is bottom of canvas
747747
if not wx.Platform == '__WXMSW__':
748748
scale = self.GetDPIScaleFactor()
749-
return x*scale, self.figure.bbox.height - y*scale
749+
return x*scale, self.get_width_height(physical=True)[1] - y*scale
750750
else:
751-
return x, self.figure.bbox.height - y
751+
return x, self.get_width_height(physical=True)[1] - y
752752

753753
def _on_key_down(self, event):
754754
"""Capture key press."""
@@ -1169,7 +1169,7 @@ def save_figure(self, *args):
11691169
dialog.Destroy()
11701170

11711171
def draw_rubberband(self, event, x0, y0, x1, y1):
1172-
height = self.canvas.figure.bbox.height
1172+
height = self.canvas.get_width_height(physical=True)[1]
11731173
sf = 1 if wx.Platform == '__WXMSW__' else self.canvas.GetDPIScaleFactor()
11741174
self.canvas._rubberband_rect = (x0/sf, (height - y0)/sf,
11751175
x1/sf, (height - y1)/sf)

0 commit comments

Comments
 (0)