8000 Merge pull request #17641 from tacaswell/qt_backports · matplotlib/matplotlib@aead584 · GitHub
[go: up one dir, main page]

Skip to content

Commit aead584

Browse files
authored
Merge pull request #17641 from tacaswell/qt_backports
Qt backports
2 parents df4c5c1 + ff32cc7 commit aead584

File tree

6 files changed

+75
-25
lines changed

6 files changed

+75
-25
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2293,7 +2293,7 @@ def start_event_loop(self, timeout=0):
22932293
The event loop blocks until a callback function triggers
22942294
`stop_event_loop`, or *timeout* is reached.
22952295
2296-
If *timeout* is negative, never timeout.
2296+
If *timeout* is 0 or negative, never timeout.
22972297
22982298
Only interactive backends need to reimplement this method and it relies
22992299
on `flush_events` being properly implemented.

lib/matplotlib/backends/backend_qt5.py

Lines changed: 9 additions & 12 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, _setDevicePixelRatioF,
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
@@ -454,8 +451,9 @@ def start_event_loop(self, timeout=0):
454451
if hasattr(self, "_event_loop") and self._event_loop.isRunning():
455452
raise RuntimeError("Event loop already running")
456453
self._event_loop = event_loop = QtCore.QEventLoop()
457-
if timeout:
458-
timer = QtCore.QTimer.singleShot(timeout * 1000, event_loop.quit)
454+
if timeout > 0:
455+
timer = QtCore.QTimer.singleShot(int(timeout * 1000),
456+
event_loop.quit)
459457
event_loop.exec_()
460458

461459
def stop_event_loop(self, event=None):
@@ -683,8 +681,7 @@ def _icon(self, name, color=None):
683681
if is_pyqt5():
684682
name = name.replace('.png', '_large.png')
685683
pm = QtGui.QPixmap(os.path.join(self.basedir, name))
686-
if hasattr(pm, 'setDevicePixelRatio'):
687-
pm.setDevicePixelRatio(self.canvas._dpi_ratio)
684+
_setDevicePixelRatioF(pm, _devicePixelRatioF(self))
688685
if color is not None:
689686
mask = pm.createMaskFromColor(QtGui.QColor('black'),
690687
QtCore.Qt.MaskOutColor)

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 3 additions & 5 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)
@@ -87,7 +85,7 @@ def blit(self, bbox=None):
8785
bbox = self.figure.bbox
8886

8987
# repaint uses logical pixels, not physical pixels like the renderer.
90-
l, b, w, h = [pt / self._dpi_ratio for pt in bbox.bounds]
88+
l, b, w, h = [int(pt / self._dpi_ratio) for pt in bbox.bounds]
9189
t = b + h
9290
self.repaint(l, self.renderer.height / self._dpi_ratio - t, w, h)
9391

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+
elif 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)
0