Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Qt backports #17641

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -2293,7 +2293,7 @@ def start_event_loop(self, timeout=0):
The event loop blocks until a callback function triggers
`stop_event_loop`, or *timeout* is reached.

If *timeout* is negative, never timeout.
If *timeout* is 0 or negative, never timeout.

Only interactive backends need to reimplement this method and it relies
on `flush_events` being properly implemented.
Expand Down
21 changes: 9 additions & 12 deletions lib/matplotlib/backends/backend_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
import matplotlib.backends.qt_editor.figureoptions as figureoptions
from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool
from matplotlib.backend_managers import ToolManager

from . import qt_compat
from .qt_compat import (
QtCore, QtGui, QtWidgets, _isdeleted, _getSaveFileName,
is_pyqt5, __version__, QT_API)
is_pyqt5, __version__, QT_API, _setDevicePixelRatioF,
_devicePixelRatioF)


backend_version = __version__

Expand Down Expand Up @@ -267,12 +269,7 @@ def _update_figure_dpi(self):

@property
def _dpi_ratio(self):
# Not available on Qt4 or some older Qt5.
try:
# self.devicePixelRatio() returns 0 in rare cases
return self.devicePixelRatio() or 1
except AttributeError:
return 1
return _devicePixelRatioF(self)

def _update_dpi(self):
# As described in __init__ above, we need to be careful in cases with
Expand Down Expand Up @@ -454,8 +451,9 @@ def start_event_loop(self, timeout=0):
if hasattr(self, "_event_loop") and self._event_loop.isRunning():
raise RuntimeError("Event loop already running")
self._event_loop = event_loop = QtCore.QEventLoop()
if timeout:
timer = QtCore.QTimer.singleShot(timeout * 1000, event_loop.quit)
if timeout > 0:
timer = QtCore.QTimer.singleShot(int(timeout * 1000),
event_loop.quit)
event_loop.exec_()

def stop_event_loop(self, event=None):
Expand Down Expand Up @@ -683,8 +681,7 @@ def _icon(self, name, color=None):
if is_pyqt5():
name = name.replace('.png', '_large.png')
pm = QtGui.QPixmap(os.path.join(self.basedir, name))
if hasattr(pm, 'setDevicePixelRatio'):
pm.setDevicePixelRatio(self.canvas._dpi_ratio)
_setDevicePixelRatioF(pm, _devicePixelRatioF(self))
if color is not None:
mask = pm.createMaskFromColor(QtGui.QColor('black'),
QtCore.Qt.MaskOutColor)
Expand Down
8 changes: 3 additions & 5 deletions lib/matplotlib/backends/backend_qt5agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .backend_qt5 import (
QtCore, QtGui, QtWidgets, _BackendQT5, FigureCanvasQT, FigureManagerQT,
NavigationToolbar2QT, backend_version)
from .qt_compat import QT_API
from .qt_compat import QT_API, _setDevicePixelRatioF


class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT):
Expand Down Expand Up @@ -64,9 +64,7 @@ def paintEvent(self, event):

qimage = QtGui.QImage(buf, buf.shape[1], buf.shape[0],
QtGui.QImage.Format_ARGB32_Premultiplied)
if hasattr(qimage, 'setDevicePixelRatio'):
# Not available on Qt4 or some older Qt5.
qimage.setDevicePixelRatio(self._dpi_ratio)
_setDevicePixelRatioF(qimage, self._dpi_ratio)
# set origin using original QT coordinates
origin = QtCore.QPoint(rect.left(), rect.top())
painter.drawImage(origin, qimage)
Expand All @@ -87,7 +85,7 @@ def blit(self, bbox=None):
bbox = self.figure.bbox

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

Expand Down
10 changes: 4 additions & 6 deletions lib/matplotlib/backends/backend_qt5cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from .backend_cairo import cairo, FigureCanvasCairo, RendererCairo
from .backend_qt5 import QtCore, QtGui, _BackendQT5, FigureCanvasQT
from .qt_compat import QT_API
from .qt_compat import QT_API, _setDevicePixelRatioF


class FigureCanvasQTCairo(FigureCanvasQT, FigureCanvasCairo):
Expand All @@ -19,8 +19,8 @@ def draw(self):
def paintEvent(self, event):
self._update_dpi()
dpi_ratio = self._dpi_ratio
width = dpi_ratio * self.width()
height = dpi_ratio * self.height()
width = int(dpi_ratio * self.width())
height = int(dpi_ratio * self.height())
if (width, height) != self._renderer.get_canvas_width_height():
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
self._renderer.set_ctx_from_surface(surface)
Expand All @@ -33,9 +33,7 @@ def paintEvent(self, event):
# QImage under PySide on Python 3.
if QT_API == 'PySide':
ctypes.c_long.from_address(id(buf)).value = 1
if hasattr(qimage, 'setDevicePixelRatio'):
# Not available on Qt4 or some older Qt5.
qimage.setDevicePixelRatio(dpi_ratio)
_setDevicePixelRatioF(qimage, dpi_ratio)
painter = QtGui.QPainter(self)
painter.eraseRect(event.rect())
painter.drawImage(0, 0, qimage)
Expand Down
36 changes: 35 additions & 1 deletion lib/matplotlib/backends/qt_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,38 @@ def is_pyqt5():
# These globals are only defined for backcompatibility purposes.
ETS = dict(pyqt=(QT_API_PYQTv2, 4), pyside=(QT_API_PYSIDE, 4),
pyqt5=(QT_API_PYQT5, 5), pyside2=(QT_API_PYSIDE2, 5))
QT_RC_MAJOR_VERSION = 5 if is_pyqt5() else 4

QT_RC_MAJOR_VERSION = int(QtCore.qVersion().split(".")[0])


def _devicePixelRatioF(obj):
"""
Return obj.devicePixelRatioF() with graceful fallback for older Qt.

This can be replaced by the direct call when we require Qt>=5.6.
"""
try:
# Not available on Qt<5.6
return obj.devicePixelRatioF() or 1
except AttributeError:
pass
try:
# Not available on Qt4 or some older Qt5.
# self.devicePixelRatio() returns 0 in rare cases
return obj.devicePixelRatio() or 1
except AttributeError:
return 1


def _setDevicePixelRatioF(obj, val):
"""
Call obj.setDevicePixelRatioF(val) with graceful fallback for older Qt.

This can be replaced by the direct call when we require Qt>=5.6.
"""
if hasattr(obj, 'setDevicePixelRatioF'):
# Not available on Qt<5.6
obj.setDevicePixelRatioF(val)
elif hasattr(obj, 'setDevicePixelRatio'):
# Not available on Qt4 or some older Qt5.
obj.setDevicePixelRatio(val)
23 changes: 23 additions & 0 deletions lib/matplotlib/tests/test_backend_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,29 @@ 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()

# The DPI and the renderer width/height change
assert fig.dpi == 180
assert qt_canvas.renderer.width == 900
assert qt_canvas.renderer.height == 360

# The actual widget size and figure physical size don't change
assert size.width() == 600
assert size.height() == 240
assert qt_canvas.get_width_height() == (600, 240)
assert (fig.get_size_inches() == (5, 2)).all()


@pytest.mark.backend('Qt5Agg')
def test_subplottool():
Expand Down