@@ -87,7 +87,6 @@ class FigureCanvasGTK3(Gtk.DrawingArea, FigureCanvasBase):
8787 | Gdk .EventMask .ENTER_NOTIFY_MASK
8888 | Gdk .EventMask .LEAVE_NOTIFY_MASK
8989 | Gdk .EventMask .POINTER_MOTION_MASK
90- | Gdk .EventMask .POINTER_MOTION_HINT_MASK
9190 | Gdk .EventMask .SCROLL_MASK )
9291
9392 def __init__ (self , figure = None ):
@@ -102,6 +101,8 @@ def __init__(self, figure=None):
102101 self .connect ('button_press_event' , self .button_press_event )
103102 self .connect ('button_release_event' , self .button_release_event )
104103 self .connect ('configure_event' , self .configure_event )
104+ self .connect ('screen-changed' , self ._update_device_pixel_ratio )
105+ self .connect ('notify::scale-factor' , self ._update_device_pixel_ratio )
105106 self .connect ('draw' , self .on_draw_event )
106107 self .connect ('draw' , self ._post_draw )
107108 self .connect ('key_press_event' , self .key_press_event )
@@ -132,26 +133,35 @@ def set_cursor(self, cursor):
132133 context = GLib .MainContext .default ()
133134 context .iteration (True )
134135
136+ def _mouse_event_coords (self , event ):
137+ """
138+ Calculate mouse coordinates in physical pixels.
139+
140+ GTK use logical pixels, but the figure is scaled to physical pixels for
141+ rendering. Transform to physical pixels so that all of the down-stream
142+ transforms work as expected.
143+
144+ Also, the origin is different and needs to be corrected.
145+ """
146+ x = event .x * self .device_pixel_ratio
147+ # flip y so y=0 is bottom of canvas
148+ y = self .figure .bbox .height - event .y * self .device_pixel_ratio
149+ return x , y
150+
135151 def scroll_event (self , widget , event ):
136- x = event .x
137- # flipy so y=0 is bottom of canvas
138- y = self .get_allocation ().height - event .y
152+ x , y = self ._mouse_event_coords (event )
139153 step = 1 if event .direction == Gdk .ScrollDirection .UP else - 1
140154 FigureCanvasBase .scroll_event (self , x , y , step , guiEvent = event )
141155 return False # finish event propagation?
142156
143157 def button_press_event (self , widget , event ):
144- x = event .x
145- # flipy so y=0 is bottom of canvas
146- y = self .get_allocation ().height - event .y
158+ x , y = self ._mouse_event_coords (event )
147159 FigureCanvasBase .button_press_event (
148160 self , x , y , event .button , guiEvent = event )
149161 return False # finish event propagation?
150162
151163 def button_release_event (self , widget , event ):
152- x = event .x
153- # flipy so y=0 is bottom of canvas
154- y = self .get_allocation ().height - event .y
164+ x , y = self ._mouse_event_coords (event )
155165 FigureCanvasBase .button_release_event (
156166 self , x , y , event .button , guiEvent = event )
157167 return False # finish event propagation?
@@ -167,29 +177,21 @@ def key_release_event(self, widget, event):
167177 return True # stop event propagation
168178
169179 def motion_notify_event (self , widget , event ):
170- if event .is_hint :
171- t , x , y , state = event .window .get_device_position (event .device )
172- else :
173- x , y = event .x , event .y
174-
175- # flipy so y=0 is bottom of canvas
176- y = self .get_allocation ().height - y
180+ x , y = self ._mouse_event_coords (event )
177181 FigureCanvasBase .motion_notify_event (self , x , y , guiEvent = event )
178182 return False # finish event propagation?
179183
180184 def leave_notify_event (self , widget , event ):
181185 FigureCanvasBase .leave_notify_event (self , event )
182186
183187 def enter_notify_event (self , widget , event ):
184- x = event .x
185- # flipy so y=0 is bottom of canvas
186- y = self .get_allocation ().height - event .y
188+ x , y = self ._mouse_event_coords (event )
187189 FigureCanvasBase .enter_notify_event (self , guiEvent = event , xy = (x , y ))
188190
189191 def size_allocate (self , widget , allocation ):
190192 dpival = self .figure .dpi
191- winch = allocation .width / dpival
192- hinch = allocation .height / dpival
193+ winch = allocation .width * self . device_pixel_ratio / dpival
194+ hinch = allocation .height * self . device_pixel_ratio / dpival
193195 self .figure .set_size_inches (winch , hinch , forward = False )
194196 FigureCanvasBase .resize_event (self )
195197 self .draw_idle ()
@@ -211,10 +213,21 @@ def _get_key(self, event):
211213 key = f'{ prefix } +{ key } '
212214 return key
213215
216+ def _update_device_pixel_ratio (self , * args , ** kwargs ):
217+ # We need to be careful in cases with mixed resolution displays if
218+ # device_pixel_ratio changes.
219+ if self ._set_device_pixel_ratio (self .get_scale_factor ()):
220+ # The easiest way to resize the canvas is to emit a resize event
221+ # since we implement all the logic for resizing the canvas for that
222+ # event.
223+ self .queue_resize ()
224+ self .queue_draw ()
225+
214226 def configure_event (self , widget , event ):
215227 if widget .get_property ("window" ) is None :
216228 return
217- w , h = event .width , event .height
229+ w = event .width * self .device_pixel_ratio
230+ h = event .height * self .device_pixel_ratio
218231 if w < 3 or h < 3 :
219232 return # empty fig
220233 # resize the figure (in inches)
@@ -231,7 +244,8 @@ def _post_draw(self, widget, ctx):
231244 if self ._rubberband_rect is None :
232245 return
233246
234- x0 , y0 , w , h = self ._rubberband_rect
247+ x0 , y0 , w , h = (dim / self .device_pixel_ratio
248+ for dim in self ._rubberband_rect )
235249 x1 = x0 + w
236250 y1 = y0 + h
237251
@@ -318,8 +332,7 @@ def __init__(self, canvas, num):
318332
319333 self .vbox .pack_start (self .canvas , True , True , 0 )
320334 # calculate size for window
321- w = int (self .canvas .figure .bbox .width )
322- h = int (self .canvas .figure .bbox .height )
335+ w , h = self .canvas .get_width_height ()
323336
324337 self .toolbar = self ._get_toolbar ()
325338
0 commit comments