8000 Use Qt events to refresh pixel ratio. · matplotlib/matplotlib@d9f57b7 · GitHub
[go: up one dir, main page]

Skip to content

Commit d9f57b7

Browse files
committed
Use Qt events to refresh pixel ratio.
With this, we don't need to check things on every draw, but can instead rely on Qt telling us to update DPI.
1 parent 93649f8 commit d9f57b7

File tree

4 files changed

+41
-53
lines changed

4 files changed

+41
-53
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,8 @@ def __init__(self, figure):
227227
self._update_figure_dpi()
228228
# In cases with mixed resolution displays, we need to be careful if the
229229
# dpi_ratio changes - in this case we need to resize the canvas
230-
# accordingly. We could watch for screenChanged events from Qt, but
231-
# the issue is that we can't guarantee this will be emitted *before*
232-
# the first paintEvent for the canvas, so instead we keep track of the
233-
# dpi_ratio value here and in paintEvent we resize the canvas if
234-
# needed.
235-
self._dpi_ratio_prev = None
230+
# accordingly.
231+
self._dpi_ratio_prev = self._dpi_ratio
236232

237233
self._draw_pending = False
238234
self._is_drawing = False
@@ -253,12 +249,10 @@ def _update_figure_dpi(self):
253249
def _dpi_ratio(self):
254250
return _devicePixelRatioF(self)
255251

256-
def _update_dpi(self):
252+
def _update_pixel_ratio(self):
257253
# As described in __init__ above, we need to be careful in cases with
258254
# mixed resolution displays if dpi_ratio is changing between painting
259255
# events.
260-
# Return whether we triggered a resizeEvent (and thus a paintEvent)
261-
# from within this function.
262256
if self._dpi_ratio != self._dpi_ratio_prev:
263257
# We need to update the figure DPI.
264258
self._update_figure_dpi()
@@ -270,8 +264,24 @@ def _update_dpi(self):
270264
self.resizeEvent(event)
271265
# resizeEvent triggers a paintEvent itself, so we exit this one
272266
# (after making sure that the event is immediately handled).
273-
return True
274-
return False
267+
268+
def _update_dpi(self, dpi):
269+
# Handler for screen DPI change events.
270+
self._update_pixel_ratio()
271+
272+
def _update_screen(self, screen):
273+
# Handler for window's screen change events.
274+
self._update_pixel_ratio()
275+
if screen is not None:
276+
screen.physicalDotsPerInchChanged.connect(self._update_dpi)
277+
screen.logicalDotsPerInchChanged.connect(self._update_dpi)
278+
279+
def showEvent(self, event):
280+
# Set up correct pixel ratio, and connect to any signal changes for it,
281+
# once the window is shown (and thus has these attributes).
282+
window = self.window().windowHandle()
283+
window.screenChanged.connect(self._update_screen)
284+
self._update_screen(window.screen())
275285

276286
def get_width_height(self):
277287
w, h = FigureCanvasBase.get_width_height(self)
@@ -364,10 +374,6 @@ def keyReleaseEvent(self, event):
364374
FigureCanvasBase.key_release_event(self, key, guiEvent=event)
365375

366376
def resizeEvent(self, event):
367-
# _dpi_ratio_prev will be set the first time the canvas is painted, and
368-
# the rendered buffer is useless before anyways.
369-
if self._dpi_ratio_prev is None:
370-
return
371377
w = event.size().width() * self._dpi_ratio
372378
h = event.size().height() * self._dpi_ratio
373379
dpival = self.figure.dpi

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ def paintEvent(self, event):
2727
In Qt, all drawing should be done inside of here when a widget is
2828
shown onscreen.
2929
"""
30-
if self._update_dpi():
31-
# The dpi update triggered its own paintEvent.
32-
return
3330
self._draw_idle() # Only does something if a draw is pending.
3431

3532
# If the canvas does not have a renderer, then give up and wait for

lib/matplotlib/backends/backend_qt5cairo.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ def draw(self):
1717
super().draw()
1818

1919
def paintEvent(self, event):
20-
self._update_dpi()
2120
dpi_ratio = self._dpi_ratio
2221
width = int(dpi_ratio * self.width())
2322
height = int(dpi_ratio * self.height())

lib/matplotlib/tests/test_backend_qt.py

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -158,32 +158,38 @@ def on_key_press(event):
158158

159159

160160
@pytest.mark.backend('Qt5Agg')
161-
def test_dpi_ratio_change():
161+
def test_pixel_ratio_change():
162162
"""
163-
Make sure that if _dpi_ratio changes, the figure dpi changes but the
163+
Make sure that if the pixel ratio changes, the figure dpi changes but the
164164
widget remains the same physical size.
165165
"""
166166

167-
prop = 'matplotlib.backends.backend_qt5.FigureCanvasQT._dpi_ratio'
168-
169-
with mock.patch(prop, new_callable=mock.PropertyMock) as p:
170-
167+
prop = 'matplotlib.backends.backend_qt5.FigureCanvasQT.devicePixelRatioF'
168+
with mock.patch(prop) as p:
171169
p.return_value = 3
172170

173171
fig = plt.figure(figsize=(5, 2), dpi=120)
174172
qt_canvas = fig.canvas
175173
qt_canvas.show()
176174

177-
from matplotlib.backends.backend_qt5 import qApp
175+
def set_pixel_ratio(ratio):
176+
p.return_value = ratio
177+
# Make sure the mocking worked
178+
assert qt_canvas._dpi_ratio == ratio
178179

179-
# Make sure the mocking worked
180-
assert qt_canvas._dpi_ratio == 3
180+
# The value here doesn't matter, as we can't mock the C++ QScreen
181+
# object, but can override the functional wrapper around it.
182+
# Emitting this event is simply to trigger the DPI change handler
183+
# in Matplotlib in the same manner that it would occur normally.
184+
screen.logicalDotsPerInchChanged.emit(96)
181185

182-
size = qt_canvas.size()
186+
qt_canvas.draw()
187+
qt_canvas.flush_events()
183188

184189
qt_canvas.manager.show()
185-
qt_canvas.draw()
186-
qApp.processEvents()
190+
size = qt_canvas.size()
191+
screen = qt_canvas.window().windowHandle().screen()
192+
set_pixel_ratio(3)
187193

188194
# The DPI and the renderer width/height change
189195
assert fig.dpi == 360
@@ -196,17 +202,7 @@ def test_dpi_ratio_change():
196202
assert qt_canvas.get_width_height() == (600, 240)
197203
assert (fig.get_size_inches() == (5, 2)).all()
198204

199-
p.return_value = 2
200-
201-
assert qt_canvas._dpi_ratio == 2
202-
203-
qt_canvas.draw()
204-
qApp.processEvents()
205-
# this second processEvents is required to fully run the draw.
206-
# On `update` we notice the DPI has changed and trigger a
207-
# resize event to refresh, the second processEvents is
208-
# required to process that and fully update the window sizes.
209-
qApp.processEvents()
205+
set_pixel_ratio(2)
210206

211207
# The DPI and the renderer width/height change
212208
assert fig.dpi == 240
@@ -219,17 +215,7 @@ def test_dpi_ratio_change():
219215
assert qt_canvas.get_width_height() == (600, 240)
220216
assert (fig.get_size_inches() == (5, 2)).all()
221217

222-
p.return_value = 1.5
223-
224-
assert qt_canvas._dpi_ratio == 1.5
225-
226-
qt_canvas.draw()
227-
qApp.processEvents()
228-
# this second processEvents is required to fully run the draw.
229-
# On `update` we notice the DPI has changed and trigger a
230-
# resize event to refresh, the second processEvents is
231-
# required to process that and fully update the window sizes.
232-
qApp.processEvents()
218+
set_pixel_ratio(1.5)
233219

234220
# The DPI and the renderer width/height change
235221
assert fig.dpi == 180

0 commit comments

Comments
 (0)
0