Skip to content

Commit bbd3cf6

Browse files
jklymaktacaswell
authored andcommitted
Backport PR #15656: Support fractional HiDpi scaling with Qt backends
Merge pull request #15656 from timhoffm/qt-fractional-hidpi Support fractional HiDpi scaling with Qt backends Conflicts: lib/matplotlib/backends/backend_qt5.py lib/matplotlib/backends/qt_compat.py - only backport relevant changes
1 parent df4c5c1 commit bbd3cf6

File tree

5 files changed

+70
-21
lines changed

5 files changed

+70
-21
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
import matplotlib.backends.qt_editor.figureoptions as figureoptions
1616
from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool
1717
from matplotlib.backend_managers import ToolManager
18-
18+
from . import qt_compat
1919
from .qt_compat import (
2020
QtCore, QtGui, QtWidgets, _isdeleted, _getSaveFileName,
21-
is_pyqt5, __version__, QT_API)
21+
is_pyqt5, __version__, QT_API,
22+
_devicePixelRatioF)
23+
2224

2325
backend_version = __version__
2426

@@ -267,12 +269,7 @@ def _update_figure_dpi(self):
267269

268270
@property
269271
def _dpi_ratio(self):
270-
# Not available on Qt4 or some older Qt5.
271-
try:
272-
# self.devicePixelRatio() returns 0 in rare cases
273-
return self.devicePixelRatio() or 1
274-
except AttributeError:
275-
return 1
272+
return _devicePixelRatioF(self)
276273

277274
def _update_dpi(self):
278275
# As described in __init__ above, we need to be careful in cases with
@@ -683,8 +680,7 @@ def _icon(self, name, color=None):
683680
if is_pyqt5():
684681
name = name.replace('.png', '_large.png')
685682
pm = QtGui.QPixmap(os.path.join(self.basedir, name))
686-
if hasattr(pm, 'setDevicePixelRatio'):
687-
pm.setDevicePixelRatio(self.canvas._dpi_ratio)
683+
qt_compat._setDevicePixelRatioF(pm, _devicePixelRatioF(self))
688684
if color is not None:
689685
mask = pm.createMaskFromColor(QtGui.QColor('black'),
690686
QtCore.Qt.MaskOutColor)

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from .backend_qt5 import (
1212
QtCore, QtGui, QtWidgets, _BackendQT5, FigureCanvasQT, FigureManagerQT,
1313
NavigationToolbar2QT, backend_version)
14-
from .qt_compat import QT_API
14+
from .qt_compat import QT_API, _setDevicePixelRatioF
1515

1616

1717
class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT):
@@ -64,9 +64,7 @@ def paintEvent(self, event):
6464

6565
qimage = QtGui.QImage(buf, buf.shape[1], buf.shape[0],
6666
QtGui.QImage.Format_ARGB32_Premultiplied)
67-
if hasattr(qimage, 'setDevicePixelRatio'):
68-
# Not available on Qt4 or some older Qt5.
69-
qimage.setDevicePixelRatio(self._dpi_ratio)
67+
_setDevicePixelRatioF(qimage, self._dpi_ratio)
7068
# set origin using original QT coordinates
7169
origin = QtCore.QPoint(rect.left(), rect.top())
7270
painter.drawImage(origin, qimage)

lib/matplotlib/backends/backend_qt5cairo.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from .backend_cairo import cairo, FigureCanvasCairo, RendererCairo
44
from .backend_qt5 import QtCore, QtGui, _BackendQT5, FigureCanvasQT
5-
from .qt_compat import QT_API
5+
from .qt_compat import QT_API, _setDevicePixelRatioF
66

77

88
class FigureCanvasQTCairo(FigureCanvasQT, FigureCanvasCairo):
@@ -19,8 +19,8 @@ def draw(self):
1919
def paintEvent(self, event):
2020
self._update_dpi()
2121
dpi_ratio = self._dpi_ratio
22-
width = dpi_ratio * self.width()
23-
height = dpi_ratio * self.height()
22+
width = int(dpi_ratio * self.width())
23+
height = int(dpi_ratio * self.height())
2424
if (width, height) != self._renderer.get_canvas_width_height():
2525
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
2626
self._renderer.set_ctx_from_surface(surface)
@@ -33,9 +33,7 @@ def paintEvent(self, event):
3333
# QImage under PySide on Python 3.
3434
if QT_API == 'PySide':
3535
ctypes.c_long.from_address(id(buf)).value = 1
36-
if hasattr(qimage, 'setDevicePixelRatio'):
37-
# Not available on Qt4 or some older Qt5.
38-
qimage.setDevicePixelRatio(dpi_ratio)
36+
_setDevicePixelRatioF(qimage, dpi_ratio)
3937
painter = QtGui.QPainter(self)
4038
painter.eraseRect(event.rect())
4139
painter.drawImage(0, 0, qimage)

lib/matplotlib/backends/qt_compat.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,38 @@ def is_pyqt5():
173173
# These globals are only defined for backcompatibility purposes.
174174
ETS = dict(pyqt=(QT_API_PYQTv2, 4), pyside=(QT_API_PYSIDE, 4),
175175
pyqt5=(QT_API_PYQT5, 5), pyside2=(QT_API_PYSIDE2, 5))
176-
QT_RC_MAJOR_VERSION = 5 if is_pyqt5() else 4
176+
177+
QT_RC_MAJOR_VERSION = int(QtCore.qVersion().split(".")[0])
178+
179+
180+
def _devicePixelRatioF(obj):
181+
"""
182+
Return obj.devicePixelRatioF() with graceful fallback for older Qt.
183+
184+
This can be replaced by the direct call when we require Qt>=5.6.
185+
"""
186+
try:
187+
# Not available on Qt<5.6
188+
return obj.devicePixelRatioF() or 1
189+
except AttributeError:
190+
pass
191+
try:
192+
# Not available on Qt4 or some older Qt5.
193+
# self.devicePixelRatio() returns 0 in rare cases
194+
return obj.devicePixelRatio() or 1
195+
except AttributeError:
196+
return 1
197+
198+
199+
def _setDevicePixelRatioF(obj, val):
200+
"""
201+
Call obj.setDevicePixelRatioF(val) with graceful fallback for older Qt.
202+
203+
This can be replaced by the direct call when we require Qt>=5.6.
204+
"""
205+
if hasattr(obj, 'setDevicePixelRatioF'):
206+
# Not available on Qt<5.6
207+
obj.setDevicePixelRatioF(val)
208+
if hasattr(obj, 'setDevicePixelRatio'):
209+
# Not available on Qt4 or some older Qt5.
210+
obj.setDevicePixelRatio(val)

lib/matplotlib/tests/test_backend_qt.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,29 @@ def test_dpi_ratio_change():
253253
assert qt_canvas.get_width_height() == (600, 240)
254254
assert (fig.get_size_inches() == (5, 2)).all()
255255

256+
p.return_value = 1.5
257+
258+
assert qt_canvas._dpi_ratio == 1.5
259+
260+
qt_canvas.draw()
261+
qApp.processEvents()
262+
# this second processEvents is required to fully run the draw.
263+
# On `update` we notice the DPI has changed and trigger a
264+
# resize event to refresh, the second processEvents is
265+
# required to process that and fully update the window sizes.
266+
qApp.processEvents()
267+
268+
# The DPI and the renderer width/height change
269+
assert fig.dpi == 180
270+
assert qt_canvas.renderer.width == 900
271+
assert qt_canvas.renderer.height == 360
272+
273+
# The actual widget size and figure physical size don't change
274+
assert size.width() == 600
275+
assert size.height() == 240
276+
assert qt_canvas.get_width_height() == (600, 240)
277+
assert (fig.get_size_inches() == (5, 2)).all()
278+
256279

257280
@pytest.mark.backend('Qt5Agg')
258281
def test_subplottool():

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy