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

Skip to content

Commit 0f42e10

Browse files
authored
Merge pull request #10210 from anntzer/qtcairo-minimal
qt{4,5}cairo backend: the minimal version.
2 parents 0e5345e + afe6c1a commit 0f42e10

33 files changed

+269
-127
lines changed

doc/api/api_changes.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ out what caused the breakage and how to fix it by updating your code.
1010
For new features that were added to Matplotlib, please see
1111
:ref:`whats-new`.
1212

13+
API Changes in 2.2.0
14+
====================
15+
16+
.. toctree::
17+
:glob:
18+
:maxdepth: 1
19+
20+
next_api_changes/*
21+
22+
1323
API Changes in 2.1.2
1424
====================
1525

doc/api/backend_qt4cairo_api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
:mod:`matplotlib.backends.backend_qt4cairo`
3+
===========================================
4+
5+
.. automodule:: matplotlib.backends.backend_qt4cairo
6+
:members:
7+
:undoc-members:
8+
:show-inheritance:

doc/api/backend_qt5cairo_api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
:mod:`matplotlib.backends.backend_qt5cairo`
3+
===========================================
4+
5+
.. automodule:: matplotlib.backends.backend_qt5cairo
6+
:members:
7+
:undoc-members:
8+
:show-inheritance:

doc/api/index_backend_api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ backends
1919
backend_pgf_api.rst
2020
backend_ps_api.rst
2121
backend_qt4agg_api.rst
22+
backend_qt4cairo_api.rst
2223
backend_qt5agg_api.rst
24+
backend_qt5cairo_api.rst
2325
backend_svg_api.rst
2426
backend_tkagg_api.rst
2527
backend_webagg_api.rst

doc/api/api_changes/2017-12-17-AL.rst renamed to doc/api/next_api_changes/2017-12-17-AL.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Removal of deprecated rcParams
22
``````````````````````````````
33

44
The following deprecated rcParams have been removed:
5+
56
- ``axes.color_cycle`` (see ``axes.prop_cycle``),
67
- ``legend.isaxes``,
78
- ``svg.embed_char_paths`` (see ``svg.fonttype``),
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
Changes to Qt backend class MRO
2+
```````````````````````````````
3+
4+
To support both Agg and cairo rendering for Qt backends all of the
5+
non-Agg specific code previously in
6+
:class:`.backend_qt5agg.FigureCanvasQTAggBase` has been moved to
7+
:class:`.backend_qt5.FigureCanvasQT` so it can be shared with the cairo
8+
implementation. The :meth:`.FigureCanvasQTAggBase.paintEvent`,
9+
:meth:`.FigureCanvasQTAggBase.blit`, and
10+
:meth:`.FigureCanvasQTAggBase.print_figure` methods have moved to
11+
:meth:`.FigureCanvasQTAgg.paintEvent`, :meth:`.FigureCanvasQTAgg.blit`, and
12+
:meth:`.FigureCanvasQTAgg.print_figure`. The first two methods assume that
13+
the instance is also a :class:`QWidget` so to use
14+
:class:`FigureCanvasQTAggBase` it was required to multiple inherit
15+
from a :class:`QWidget` sub-class.
16+
17+
Having moved all of its methods either up or down the class hierarchy
18+
:class:`FigureCanvasQTAggBase` has been deprecated. To do this with
19+
out warning and to preserve as much API as possible,
20+
:class:`.backend_qt5.FigureCanvasQTAggBase` now inherits from
21+
:class:`.backend_qt5.FigureCanvasQTAgg`.
22+
23+
The MRO for :class:`FigureCanvasQTAgg` and
24+
:class:`FigureCanvasQTAggBase` used to be ::
25+
26+
27+
[matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg,
28+
matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase,
29+
matplotlib.backends.backend_agg.FigureCanvasAgg,
30+
matplotlib.backends.backend_qt5.FigureCanvasQT,
31+
PyQt5.QtWidgets.QWidget,
32+
PyQt5.QtCore.QObject,
33+
sip.wrapper,
34+
PyQt5.QtGui.QPaintDevice,
35+
sip.simplewrapper,
36+
matplotlib.backend_bases.FigureCanvasBase,
37+
object]
38+
39+
and ::
40+
41+
42+
[matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase,
43+
matplotlib.backends.backend_agg.FigureCanvasAgg,
44+
matplotlib.backend_bases.FigureCanvasBase,
45+
object]
46+
47+
48+
respectively. They are now ::
49+
50+
[matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg,
51+
matplotlib.backends.backend_agg.FigureCanvasAgg,
52+
matplotlib.backends.backend_qt5.FigureCanvasQT,
53+
PyQt5.QtWidgets.QWidget,
54+
PyQt5.QtCore.QObject,
55+
sip.wrapper,
56+
PyQt5.QtGui.QPaintDevice,
57+
sip.simplewrapper,
58+
matplotlib.backend_bases.FigureCanvasBase,
59+
object]
60+
61+
and ::
62+
63+
[matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase,
64+
matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg,
65+
matplotlib.backends.backend_agg.FigureCanvasAgg,
66+
matplotlib.backends.backend_qt5.FigureCanvasQT,
67+
PyQt5.QtWidgets.QWidget,
68+
PyQt5.QtCore.QObject,
69+
sip.wrapper,
70+
PyQt5.QtGui.QPaintDevice,
71+
sip.simplewrapper,
72+
matplotlib.backend_bases.FigureCanvasBase,
73+
object]

doc/api/api_changes/README.rst renamed to doc/api/next_api_changes/README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
Adding API change notes
2+
```````````````````````
3+
4+
15
For changes which require an entry in `api_changes.rst` please create
26
a file in this folder with the name :file:`YYYY-MM-DD-[initials].rst`
37
(ex :file:`2014-07-31-TAC.rst`) with contents following the form: ::
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Cairo rendering for Qt canvases
2+
-------------------------------
3+
4+
The new ``Qt4Cairo`` and ``Qt5Cairo`` backends allow Qt canvases to use Cairo
5+
rendering instead of Agg.

lib/matplotlib/backends/backend_agg.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ def draw(self):
429429
# if toolbar:
430430
# toolbar.set_cursor(cursors.WAIT)
431431
self.figure.draw(self.renderer)
432+
# A GUI class may be need to update a window using this draw, so
433+
# don't forget to call the superclass.
434+
super(FigureCanvasAgg, self).draw()
432435
finally:
433436
# if toolbar:
434437
# toolbar.set_cursor(toolbar._lastCursor)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .backend_qt5cairo import _BackendQT5Cairo
2+
3+
4+
@_BackendQT5Cairo.export
5+
class _BackendQT4Cairo(_BackendQT5Cairo):
6+
pass

lib/matplotlib/backends/backend_qt5.py

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import signal
99
import sys
1010
from six import unichr
11+
import traceback
1112

1213
import matplotlib
1314

@@ -226,19 +227,16 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
226227
# QtCore.Qt.XButton2: None,
227228
}
228229

229-
def _update_figure_dpi(self):
230-
dpi = self._dpi_ratio * self.figure._original_dpi
231-
self.figure._set_dpi(dpi, forward=False)
232-
233230
@_allow_super_init
234231
def __init__(self, figure):
235232
_create_qApp()
236233
super(FigureCanvasQT, self).__init__(figure=figure)
237234

238-
figure._original_dpi = figure.dpi
239235
self.figure = figure
236+
# We don't want to scale up the figure DPI more than once.
237+
# Note, we don't handle a signal for changing DPI yet.
238+
figure._original_dpi = figure.dpi
240239
self._update_figure_dpi()
241-
self.resize(*self.get_width_height())
242240
# In cases with mixed resolution displays, we need to be careful if the
243241
# dpi_ratio changes - in this case we need to resize the canvas
244242
# accordingly. We could watch for screenChanged events from Qt, but
@@ -248,13 +246,23 @@ def __init__(self, figure):
248246
# needed.
249247
self._dpi_ratio_prev = None
250248

249+
self._draw_pending = False
250+
self._is_drawing = False
251+
self._draw_rect_callback = lambda painter: None
252+
253+
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
251254
self.setMouseTracking(True)
255+
self.resize(*self.get_width_height())
252256
# Key auto-repeat enabled by default
253257
self._keyautorepeat = True
254258

255259
palette = QtGui.QPalette(QtCore.Qt.white)
256260
self.setPalette(palette)
257261

262+
def _update_figure_dpi(self):
263+
dpi = self._dpi_ratio * self.figure._original_dpi
264+
self.figure._set_dpi(dpi, forward=False)
265+
258266
@property
259267
def _dpi_ratio(self):
260268
# Not available on Qt4 or some older Qt5.
@@ -263,6 +271,26 @@ def _dpi_ratio(self):
263271
except AttributeError:
264272
return 1
265273

274+
def _update_dpi(self):
275+
# As described in __init__ above, we need to be careful in cases with
276+
# mixed resolution displays if dpi_ratio is changing between painting
277+
# events.
278+
# Return whether we triggered a resizeEvent (and thus a paintEvent)
279+
# from within this function.
280+
if self._dpi_ratio != self._dpi_ratio_prev:
281+
# We need to update the figure DPI.
282+
self._update_figure_dpi()
283+
self._dpi_ratio_prev = self._dpi_ratio
284+
# The easiest way to resize the canvas is to emit a resizeEvent
285+
# since we implement all the logic for resizing the canvas for
286+
# that event.
287+
event = QtGui.QResizeEvent(self.size(), self.size())
288+
self.resizeEvent(event)
289+
# resizeEvent triggers a paintEvent itself, so we exit this one
290+
# (after making sure that the event is immediately handled).
291+
return True
292+
return False
293+
266294
def get_width_height(self):
267295
w, h = FigureCanvasBase.get_width_height(self)
268296
return int(w / self._dpi_ratio), int(h / self._dpi_ratio)
@@ -453,6 +481,60 @@ def stop_event_loop(self, event=None):
453481
if hasattr(self, "_event_loop"):
454482
self._event_loop.quit()
455483

484+
def draw(self):
485+
"""Render the figure, and queue a request for a Qt draw.
486+
"""
487+
# The renderer draw is done here; delaying causes problems with code
488+
# that uses the result of the draw() to update plot elements.
489+
if self._is_drawing:
490+
return
491+
self._is_drawing = True
492+
try:
493+
super(FigureCanvasQT, self).draw()
494+
finally:
495+
self._is_drawing = False
496+
self.update()
497+
498+
def draw_idle(self):
499+
"""Queue redraw of the Agg buffer and request Qt paintEvent.
500+
"""
501+
# The Agg draw needs to be handled by the same thread matplotlib
502+
# modifies the scene graph from. Post Agg draw request to the
503+
# current event loop in order to ensure thread affinity and to
504+
# accumulate multiple draw requests from event handling.
505+
# TODO: queued signal connection might be safer than singleShot
506+
if not (self._draw_pending or self._is_drawing):
507+
self._draw_pending = True
508+
QtCore.QTimer.singleShot(0, self._draw_idle)
509+
510+
def _draw_idle(self):
511+
if self.height() < 0 or self.width() < 0:
512+
self._draw_pending = False
513+
if not self._draw_pending:
514+
return
515+
try:
516+
self.draw()
517+
except Exception:
518+
# Uncaught exceptions are fatal for PyQt5, so catch them instead.
519+
traceback.print_exc()
520+
finally:
521+
self._draw_pending = False
522+
523+
def drawRectangle(self, rect):
524+
# Draw the zoom rectangle to the QPainter. _draw_rect_callback needs
525+
# to be called at the end of paintEvent.
526+
if rect is not None:
527+
def _draw_rect_callback(painter):
528+
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
529+
QtCore.Qt.DotLine)
530+
painter.setPen(pen)
531+
painter.drawRect(*(pt / self._dpi_ratio for pt in rect))
532+
else:
533+
def _draw_rect_callback(painter):
534+
return
535+
self._draw_rect_callback = _draw_rect_callback
536+
self.update()
537+
456538

457539
class MainWindow(QtWidgets.QMainWindow):
458540
closing = QtCore.Signal()

0 commit comments

Comments
 (0)