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

Skip to content

Commit 51cfbd8

Browse files
authored
Merge pull request #8931 from astrofrog/qt5-fix-resolution-change
Fix a bug with the Qt5 backend with mixed resolution displays
2 parents c4d7ce4 + 591a9e0 commit 51cfbd8

File tree

4 files changed

+103
-8
lines changed

4 files changed

+103
-8
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ def save_figure(self, *args):
674674

675675
fname, filter = _getSaveFileName(self.parent,
676676
"Choose a filename to save to",
677-
start, filters, selectedFilter)
677+
start, filters, selectedFilter)
678678
if fname:
679679
if startpath == '':
680680
# explicitly missing key or empty str signals to use cwd

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ def __init__(self, figure):
3838
self._bbox_queue = []
3939
self._drawRect = None
4040

41+
# In cases with mixed resolution displays, we need to be careful if the
42+
# dpi_ratio changes - in this case we need to resize the canvas
43+
# accordingly. We could watch for screenChanged events from Qt, but
44+
# the issue is that we can't guarantee this will be emitted *before*
45+
# the first paintEvent for the canvas, so instead we keep track of the
46+
# dpi_ratio value here and in paintEvent we resize the canvas if
47+
# needed.
48+
self._dpi_ratio_prev = None
49+
4150
def drawRectangle(self, rect):
4251
if rect is not None:
4352
self._drawRect = [pt / self._dpi_ratio for pt in rect]
@@ -56,11 +65,29 @@ def paintEvent(self, e):
5665
In Qt, all drawing should be done inside of here when a widget is
5766
shown onscreen.
5867
"""
68+
5969
# if the canvas does not have a renderer, then give up and wait for
6070
# FigureCanvasAgg.draw(self) to be called
6171
if not hasattr(self, 'renderer'):
6272
return
6373

74+
# As described in __init__ above, we need to be careful in cases with
75+
# mixed resolution displays if dpi_ratio is changing between painting
76+
# events.
77+
if (self._dpi_ratio_prev is None or
78+
self._dpi_ratio != self._dpi_ratio_prev):
79+
# We need to update the figure DPI
80+
self._update_figure_dpi()
81+
# The easiest way to resize the canvas is to emit a resizeEvent
82+
# since we implement all the logic for resizing the canvas for
83+
# that event.
84+
event = QtGui.QResizeEvent(self.size(), self.size())
85+
# We use self.resizeEvent here instead of QApplication.postEvent
86+
# since the latter doesn't guarantee that the event will be emitted
87+
# straight away, and this causes visual delays in the changes.
88+
self.resizeEvent(event)
89+
self._dpi_ratio_prev = self._dpi_ratio
90+
6491
painter = QtGui.QPainter(self)
6592

6693
if self._bbox_queue:
@@ -167,9 +194,12 @@ def __init__(self, figure):
167194
super(FigureCanvasQTAgg, self).__init__(figure=figure)
168195
# We don't want to scale up the figure DPI more than once.
169196
# Note, we don't handle a signal for changing DPI yet.
170-
if not hasattr(self.figure, '_original_dpi'):
171-
self.figure._original_dpi = self.figure.dpi
172-
self.figure.dpi = self._dpi_ratio * self.figure._original_dpi
197+
self.figure._original_dpi = self.figure.dpi
198+
self._update_figure_dpi()
199+
200+
def _update_figure_dpi(self):
201+
dpi = self._dpi_ratio * self.figure._original_dpi
202+
self.figure._set_dpi(dpi, forward=False)
173203

174204

175205
@_BackendQT5.export

lib/matplotlib/figure.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,11 +418,16 @@ def _get_axes(self):
418418
def _get_dpi(self):
419419
return self._dpi
420420

421-
def _set_dpi(self, dpi):
421+
def _set_dpi(self, dpi, forward=True):
422+
"""
423+
The forward kwarg is passed on to set_size_inches
424+
"""
422425
self._dpi = dpi
423426
self.dpi_scale_trans.clear().scale(dpi, dpi)
424-
self.set_size_inches(*self.get_size_inches())
427+
w, h = self.get_size_inches()
428+
self.set_size_inches(w, h, forward=forward)
425429
self.callbacks.process('dpi_changed', self)
430+
426431
dpi = property(_get_dpi, _set_dpi)
427432

428433
def get_tight_layout(self):

lib/matplotlib/tests/test_backend_qt5.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from __future__ import (absolute_import, division, print_function,
22
unicode_literals)
33

4+
import copy
5+
6+
import matplotlib
47
from matplotlib import pyplot as plt
58
from matplotlib._pylab_helpers import Gcf
6-
import matplotlib
7-
import copy
9+
10+
from numpy.testing import assert_equal
811

912
import pytest
1013
try:
@@ -95,3 +98,60 @@ def receive(event):
9598

9699
qt_canvas.mpl_connect('key_press_event', receive)
97100
qt_canvas.keyPressEvent(event)
101+
102+
103+
@pytest.mark.backend('Qt5Agg')
104+
def test_dpi_ratio_change():
105+
"""
106+
Make sure that if _dpi_ratio changes, the figure dpi changes but the
107+
widget remains the same physical size.
108+
"""
109+
110+
prop = 'matplotlib.backends.backend_qt5.FigureCanvasQT._dpi_ratio'
111+
112+
with mock.patch(prop, new_callable=mock.PropertyMock) as p:
113+
114+
p.return_value = 3
115+
116+
fig = plt.figure(figsize=(5, 2), dpi=120)
117+
qt_canvas = fig.canvas
118+
qt_canvas.show()
119+
120+
from matplotlib.backends.backend_qt5 import qApp
121+
122+
# Make sure the mocking worked
123+
assert qt_canvas._dpi_ratio == 3
124+
125+
size = qt_canvas.size()
126+
127+
qt_canvas.manager.show()
128+
qApp.processEvents()
129+
130+
# The DPI and the renderer width/height change
131+
assert fig.dpi == 360
132+
assert qt_canvas.renderer.width == 1800
133+
assert qt_canvas.renderer.height == 720
134+
135+
# The actual widget size and figure physical size don't change
136+
assert size.width() == 200
137+
assert size.height() == 80
138+
assert_equal(qt_canvas.get_width_height(), (600, 240))
139+
assert_equal(fig.get_size_inches(), (5, 2))
140+
141+
p.return_value = 2
142+
143+
assert qt_canvas._dpi_ratio == 2
144+
145+
qt_canvas.draw()
146+
qApp.processEvents()
147+
148+
# The DPI and the renderer width/height change
149+
assert fig.dpi == 240
150+
assert qt_canvas.renderer.width == 1200
151+
assert qt_canvas.renderer.height == 480
152+
153+
# The actual widget size and figure physical size don't change
154+
assert size.width() == 200
155+
assert size.height() == 80
156+
assert_equal(qt_canvas.get_width_height(), (600, 240))
157+
assert_equal(fig.get_size_inches(), (5, 2))

0 commit comments

Comments
 (0)