8000 Merge pull request #19123 from QuLogic/qt-screen-change · matplotlib/matplotlib@081efb4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 081efb4

Browse files
authored
Merge pull request #19123 from QuLogic/qt-screen-change
Use Qt events to refresh pixel ratio.
2 parents ecba1d2 + 7bc17ad commit 081efb4

File tree

4 files changed

+39
-56
lines changed

4 files changed

+39
-56
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 19 additions & 18 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,9 @@ def _update_figure_dpi(self):
253249
def _dpi_ratio(self):
254250
return _devicePixelRatioF(self)
255251

256-
def _update_dpi(self):
257-
# As described in __init__ above, we need to be careful in cases with
258-
# mixed resolution displays if dpi_ratio is changing between painting
259-
# events.
260-
# Return whether we triggered a resizeEvent (and thus a paintEvent)
261-
# from within this function.
252+
def _update_pixel_ratio(self):
253+
# We need to be careful in cases with mixed resolution displays if
254+
# dpi_ratio changes.
262255
if self._dpi_ratio != self._dpi_ratio_prev:
263256
# We need to update the figure DPI.
264257
self._update_figure_dpi()
@@ -270,8 +263,20 @@ def _update_dpi(self):
270263
self.resizeEvent(event)
271264
# resizeEvent triggers a paintEvent itself, so we exit this one
272265
# (after making sure that the event is immediately handled).
273-
return True
274-
return False
266+
267+
def _update_screen(self, screen):
268+
# Handler for changes to a window's attached screen.
269+
self._update_pixel_ratio()
270+
if screen is not None:
271+
screen.physicalDotsPerInchChanged.connect(self._update_pixel_ratio)
272+
screen.logicalDotsPerInchChanged.connect(self._update_pixel_ratio)
273+
274+
def showEvent(self, event):
275+
# Set up correct pixel ratio, and connect to any signal changes for it,
276+
# once the window is shown (and thus has these attributes).
277+
window = self.window().windowHandle()
278+
window.screenChanged.connect(self._update_screen)
279+
self._update_screen(window.screen())
275280

276281
def get_width_height(self):
277282
w, h = FigureCanvasBase.get_width_height(self)
@@ -364,10 +369,6 @@ def keyReleaseEvent(self, event):
364369
FigureCanvasBase.key_release_event(self, key, guiEvent=event)
365370

366371
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
371372
w = event.size().width() * self._dpi_ratio
372373
h = event.size().height() * self._dpi_ratio
373374
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