@@ -93,7 +93,6 @@ class FigureCanvasGTK3(Gtk.DrawingArea, FigureCanvasBase):
9393 | Gdk .EventMask .ENTER_NOTIFY_MASK
9494 | Gdk .EventMask .LEAVE_NOTIFY_MASK
9595 | Gdk .EventMask .POINTER_MOTION_MASK
96- | Gdk .EventMask .POINTER_MOTION_HINT_MASK
9796 | Gdk .EventMask .SCROLL_MASK )
9897
9998 def __init__ (self , figure = None ):
@@ -108,6 +107,8 @@ def __init__(self, figure=None):
108107 self .connect ('button_press_event' , self .button_press_event )
109108 self .connect ('button_release_event' , self .button_release_event )
110109 self .connect ('configure_event' , self .configure_event )
110+ self .connect ('screen-changed' , self ._update_device_pixel_ratio )
111+ self .connect ('notify::scale-factor' , self ._update_device_pixel_ratio )
111112 self .connect ('draw' , self .on_draw_event )
112113 self .connect ('draw' , self ._post_draw )
113114 self .connect ('key_press_event' , self .key_press_event )
@@ -138,26 +139,35 @@ def set_cursor(self, cursor):
138139 context = GLib .MainContext .default ()
139140 context .iteration (True )
140141
142+ def _mouse_event_coords (self , event ):
143+ """
144+ Calculate mouse coordinates in physical pixels.
145+
146+ GTK use logical pixels, but the figure is scaled to physical pixels for
147+ rendering. Transform to physical pixels so that all of the down-stream
148+ transforms work as expected.
149+
150+ Also, the origin is different and needs to be corrected.
151+ """
152+ x = event .x * self .device_pixel_ratio
153+ # flip y so y=0 is bottom of canvas
154+ y = self .figure .bbox .height - event .y * self .device_pixel_ratio
155+ return x , y
156+
141157 def scroll_event (self , widget , event ):
142- x = event .x
143- # flipy so y=0 is bottom of canvas
144- y = self .get_allocation ().height - event .y
158+ x , y = self ._mouse_event_coords (event )
145159 step = 1 if event .direction == Gdk .ScrollDirection .UP else - 1
146160 FigureCanvasBase .scroll_event (self , x , y , step , guiEvent = event )
147161 return False # finish event propagation?
148162
149163 def button_press_event (self , widget , event ):
150- x = event .x
151- # flipy so y=0 is bottom of canvas
152- y = self .get_allocation ().height - event .y
164+ x , y = self ._mouse_event_coords (event )
153165 FigureCanvasBase .button_press_event (
154166 self , x , y , event .button , guiEvent = event )
155167 return False # finish event propagation?
156168
157169 def button_release_event (self , widget , event ):
158- x = event .x
159- # flipy so y=0 is bottom of canvas
160- y = self .get_allocation ().height - event .y
170+ x , y = self ._mouse_event_coords (event )
161171 FigureCanvasBase .button_release_event (
162172 self , x , y , event .button , guiEvent = event )
163173 return False # finish event propagation?
@@ -173,29 +183,21 @@ def key_release_event(self, widget, event):
173183 return True # stop event propagation
174184
175185 def motion_notify_event (self , widget , event ):
176- if event .is_hint :
177- t , x , y , state = event .window .get_device_position (event .device )
178- else :
179- x , y = event .x , event .y
180-
181- # flipy so y=0 is bottom of canvas
182- y = self .get_allocation ().height - y
186+ x , y = self ._mouse_event_coords (event )
183187 FigureCanvasBase .motion_notify_event (self , x , y , guiEvent = event )
184188 return False # finish event propagation?
185189
186190 def leave_notify_event (self , widget , event ):
187191 FigureCanvasBase .leave_notify_event (self , event )
188192
189193 def enter_notify_event (self , widget , event ):
190- x = event .x
191- # flipy so y=0 is bottom of canvas
192- y = self .get_allocation ().height - event .y
194+ x , y = self ._mouse_event_coords (event )
193195 FigureCanvasBase .enter_notify_event (self , guiEvent = event , xy = (x , y ))
194196
195197 def size_allocate (self , widget , allocation ):
196198 dpival = self .figure .dpi
197- winch = allocation .width / dpival
198- hinch = allocation .height / dpival
199+ winch = allocation .width * self . device_pixel_ratio / dpival
200+ hinch = allocation .height * self . device_pixel_ratio / dpival
199201 self .figure .set_size_inches (winch , hinch , forward = False )
200202 FigureCanvasBase .resize_event (self )
201203 self .draw_idle ()
@@ -217,10 +219,21 @@ def _get_key(self, event):
217219 key = f'{ prefix } +{ key } '
218220 return key
219221
222+ def _update_device_pixel_ratio (self , * args , ** kwargs ):
223+ # We need to be careful in cases with mixed resolution displays if
224+ # device_pixel_ratio changes.
225+ if self ._set_device_pixel_ratio (self .get_scale_factor ()):
226+ # The easiest way to resize the canvas is to emit a resize event
227+ # since we implement all the logic for resizing the canvas for that
228+ # event.
229+ self .queue_resize ()
230+ self .queue_draw ()
231+
220232 def configure_event (self , widget , event ):
221233 if widget .get_property ("window" ) is None :
222234 return
223- w , h = event .width , event .height
235+ w = event .width * self .device_pixel_ratio
236+ h = event .height * self .device_pixel_ratio
224237 if w < 3 or h < 3 :
225238 return # empty fig
226239 # resize the figure (in inches)
@@ -237,7 +250,8 @@ def _post_draw(self, widget, ctx):
237250 if self ._rubberband_rect is None :
238251 return
239252
240- x0 , y0 , w , h = self ._rubberband_rect
253+ x0 , y0 , w , h = (dim / self .device_pixel_ratio
254+ for dim in self ._rubberband_rect )
241255 x1 = x0 + w
242256 y1 = y0 + h
243257
@@ -326,8 +340,7 @@ def __init__(self, canvas, num):
326340
327341 self .vbox .pack_start (self .canvas , True , True , 0 )
328342 # calculate size for window
329- w = int (self .canvas .figure .bbox .width )
330- h = int (self .canvas .figure .bbox .height )
343+ w , h = self .canvas .get_width_height ()
331344
332345 self .toolbar = self ._get_toolbar ()
333346
0 commit comments