From ff5c84268fb9df153920bc888174cb33af82f655 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 23 Jul 2025 03:59:34 -0400 Subject: [PATCH] qt: Use better devicePixelRatio event to refresh scaling With Qt 6.6, there is an event on the window that signals when the devicePixelRatio has changed. This is better than before when we had to rely on the underlying `QScreen`, which doesn't correctly refresh when a fractional scale is used. Fixes #30218 --- lib/matplotlib/backends/backend_qt.py | 13 +++++++++++-- lib/matplotlib/tests/test_backend_qt.py | 21 ++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py index 9089e982cea6..401ce0b0b754 100644 --- a/lib/matplotlib/backends/backend_qt.py +++ b/lib/matplotlib/backends/backend_qt.py @@ -262,12 +262,21 @@ def _update_screen(self, screen): screen.physicalDotsPerInchChanged.connect(self._update_pixel_ratio) screen.logicalDotsPerInchChanged.connect(self._update_pixel_ratio) + def eventFilter(self, source, event): + if event.type() == QtCore.QEvent.Type.DevicePixelRatioChange: + self._update_pixel_ratio() + return super().eventFilter(source, event) + 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()) + current_version = tuple(int(x) for x in QtCore.qVersion().split('.', 2)[:2]) + if current_version >= (6, 6): + window.installEventFilter(self) + else: + window.screenChanged.connect(self._update_screen) + self._update_screen(window.screen()) def set_cursor(self, cursor): # docstring inherited diff --git a/lib/matplotlib/tests/test_backend_qt.py b/lib/matplotlib/tests/test_backend_qt.py index a17e98d70484..6e147fd14380 100644 --- a/lib/matplotlib/tests/test_backend_qt.py +++ b/lib/matplotlib/tests/test_backend_qt.py @@ -137,7 +137,7 @@ def on_key_press(event): @pytest.mark.backend('QtAgg', skip_on_importerror=True) -def test_device_pixel_ratio_change(): +def test_device_pixel_ratio_change(qt_core): """ Make sure that if the pixel ratio changes, the figure dpi changes but the widget remains the same logical size. @@ -154,11 +154,19 @@ def test_device_pixel_ratio_change(): def set_device_pixel_ratio(ratio): p.return_value = ratio - # 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) + window = qt_canvas.window().windowHandle() + current_version = tuple( + int(x) for x in qt_core.qVersion().split('.', 2)[:2]) + if current_version >= (6, 6): + qt_core.QCoreApplication.sendEvent( + window, + qt_core.QEvent(qt_core.QEvent.Type.DevicePixelRatioChange)) + else: + # 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. + window.screen().logicalDotsPerInchChanged.emit(96) qt_canvas.draw() qt_canvas.flush_events() @@ -168,7 +176,6 @@ def set_device_pixel_ratio(ratio): qt_canvas.manager.show() size = qt_canvas.size() - screen = qt_canvas.window().windowHandle().screen() set_device_pixel_ratio(3) # The DPI and the renderer width/height change