diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index c27c876373a0..3abe934a79f9 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -227,12 +227,8 @@ def __init__(self, figure): self._update_figure_dpi() # In cases with mixed resolution displays, we need to be careful if the # dpi_ratio changes - in this case we need to resize the canvas - # accordingly. We could watch for screenChanged events from Qt, but - # the issue is that we can't guarantee this will be emitted *before* - # the first paintEvent for the canvas, so instead we keep track of the - # dpi_ratio value here and in paintEvent we resize the canvas if - # needed. - self._dpi_ratio_prev = None + # accordingly. + self._dpi_ratio_prev = self._dpi_ratio self._draw_pending = False self._is_drawing = False @@ -253,12 +249,9 @@ def _update_figure_dpi(self): def _dpi_ratio(self): return _devicePixelRatioF(self) - def _update_dpi(self): - # As described in __init__ above, we need to be careful in cases with - # mixed resolution displays if dpi_ratio is changing between painting - # events. - # Return whether we triggered a resizeEvent (and thus a paintEvent) - # from within this function. + def _update_pixel_ratio(self): + # We need to be careful in cases with mixed resolution displays if + # dpi_ratio changes. if self._dpi_ratio != self._dpi_ratio_prev: # We need to update the figure DPI. self._update_figure_dpi() @@ -270,8 +263,20 @@ def _update_dpi(self): self.resizeEvent(event) # resizeEvent triggers a paintEvent itself, so we exit this one # (after making sure that the event is immediately handled). - return True - return False + + def _update_screen(self, screen): + # Handler for changes to a window's attached screen. + self._update_pixel_ratio() + if screen is not None: + screen.physicalDotsPerInchChanged.connect(self._update_pixel_ratio) + screen.logicalDotsPerInchChanged.connect(self._update_pixel_ratio) + + def showEvent(self, event): + # Set up correct pixel ratio, and connect to any signal changes for it, + # once the window is shown (and thus has these attributes). + window = self.window().windowHandle() + window.screenChanged.connect(self._update_screen) + self._update_screen(window.screen()) def get_width_height(self): w, h = FigureCanvasBase.get_width_height(self) @@ -364,10 +369,6 @@ def keyReleaseEvent(self, event): FigureCanvasBase.key_release_event(self, key, guiEvent=event) def resizeEvent(self, event): - # _dpi_ratio_prev will be set the first time the canvas is painted, and - # the rendered buffer is useless before anyways. - if self._dpi_ratio_prev is None: - return w = event.size().width() * self._dpi_ratio h = event.size().height() * self._dpi_ratio dpival = self.figure.dpi diff --git a/lib/matplotlib/backends/backend_qt5agg.py b/lib/matplotlib/backends/backend_qt5agg.py index 80e670b3f3e4..90eafca5334d 100644 --- a/lib/matplotlib/backends/backend_qt5agg.py +++ b/lib/matplotlib/backends/backend_qt5agg.py @@ -27,9 +27,6 @@ def paintEvent(self, event): In Qt, all drawing should be done inside of here when a widget is shown onscreen. """ - if self._update_dpi(): - # The dpi update triggered its own paintEvent. - return self._draw_idle() # Only does something if a draw is pending. # If the canvas does not have a renderer, then give up and wait for diff --git a/lib/matplotlib/backends/backend_qt5cairo.py b/lib/matplotlib/backends/backend_qt5cairo.py index c79b0bc22260..a375d1f4d1e2 100644 --- a/lib/matplotlib/backends/backend_qt5cairo.py +++ b/lib/matplotlib/backends/backend_qt5cairo.py @@ -17,7 +17,6 @@ def draw(self): super().draw() def paintEvent(self, event): - self._update_dpi() dpi_ratio = self._dpi_ratio width = int(dpi_ratio * self.width()) height = int(dpi_ratio * self.height()) diff --git a/lib/matplotlib/tests/test_backend_qt.py b/lib/matplotlib/tests/test_backend_qt.py index ab90da74d51e..afc6711158a5 100644 --- a/lib/matplotlib/tests/test_backend_qt.py +++ b/lib/matplotlib/tests/test_backend_qt.py @@ -158,32 +158,38 @@ def on_key_press(event): @pytest.mark.backend('Qt5Agg') -def test_dpi_ratio_change(): +def test_pixel_ratio_change(): """ - Make sure that if _dpi_ratio changes, the figure dpi changes but the + Make sure that if the pixel ratio changes, the figure dpi changes but the widget remains the same physical size. """ - prop = 'matplotlib.backends.backend_qt5.FigureCanvasQT._dpi_ratio' - - with mock.patch(prop, new_callable=mock.PropertyMock) as p: - + prop = 'matplotlib.backends.backend_qt5.FigureCanvasQT.devicePixelRatioF' + with mock.patch(prop) as p: p.return_value = 3 fig = plt.figure(figsize=(5, 2), dpi=120) qt_canvas = fig.canvas qt_canvas.show() - from matplotlib.backends.backend_qt5 import qApp + def set_pixel_ratio(ratio): + p.return_value = ratio + # Make sure the mocking worked + assert qt_canvas._dpi_ratio == ratio - # Make sure the mocking worked - assert qt_canvas._dpi_ratio == 3 + # The value here doesn't matter, as we can't mock the C++ QScreen + # object, but can override the functional wrapper around it. + # Emitting this event is simply to trigger the DPI change handler + # in Matplotlib in the same manner that it would occur normally. + screen.logicalDotsPerInchChanged.emit(96) - size = qt_canvas.size() + qt_canvas.draw() + qt_canvas.flush_events() qt_canvas.manager.show() - qt_canvas.draw() - qApp.processEvents() + size = qt_canvas.size() + screen = qt_canvas.window().windowHandle().screen() + set_pixel_ratio(3) # The DPI and the renderer width/height change assert fig.dpi == 360 @@ -196,17 +202,7 @@ def test_dpi_ratio_change(): assert qt_canvas.get_width_height() == (600, 240) assert (fig.get_size_inches() == (5, 2)).all() - p.return_value = 2 - - assert qt_canvas._dpi_ratio == 2 - - qt_canvas.draw() - qApp.processEvents() - # this second processEvents is required to fully run the draw. - # On `update` we notice the DPI has changed and trigger a - # resize event to refresh, the second processEvents is - # required to process that and fully update the window sizes. - qApp.processEvents() + set_pixel_ratio(2) # The DPI and the renderer width/height change assert fig.dpi == 240 @@ -219,17 +215,7 @@ def test_dpi_ratio_change(): assert qt_canvas.get_width_height() == (600, 240) assert (fig.get_size_inches() == (5, 2)).all() - p.return_value = 1.5 - - assert qt_canvas._dpi_ratio == 1.5 - - qt_canvas.draw() - qApp.processEvents() - # this second processEvents is required to fully run the draw. - # On `update` we notice the DPI has changed and trigger a - # resize event to refresh, the second processEvents is - # required to process that and fully update the window sizes. - qApp.processEvents() + set_pixel_ratio(1.5) # The DPI and the renderer width/height change assert fig.dpi == 180
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: