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

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 8 additions & 0 deletions doc/api/backend_qt4cairo_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

:mod:`matplotlib.backends.backend_qt4cairo`
===========================================

.. automodule:: matplotlib.backends.backend_qt4cairo
:members:
:undoc-members:
:show-inheritance:
8 changes: 8 additions & 0 deletions doc/api/backend_qt5cairo_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

:mod:`matplotlib.backends.backend_qt5cairo`
===========================================

.. automodule:: matplotlib.backends.backend_qt5cairo
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions doc/api/index_backend_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ backends
backend_pgf_api.rst
backend_ps_api.rst
backend_qt4agg_api.rst
backend_qt4cairo_api.rst
backend_qt5agg_api.rst
backend_qt5cairo_api.rst
backend_svg_api.rst
backend_tkagg_api.rst
backend_webagg_api.rst
Expand Down
5 changes: 5 additions & 0 deletions doc/users/next_whats_new/more-cairo.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Cairo rendering for Qt canvases
-------------------------------

The new ``Qt4Cairo`` and ``Qt5Cairo`` backends allow Qt canvases to use Cairo
rendering instead of Agg.
3 changes: 3 additions & 0 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,9 @@ def draw(self):
# if toolbar:
# toolbar.set_cursor(cursors.WAIT)
self.figure.draw(self.renderer)
# A GUI class may be need to update a window using this draw, so
# don't forget to call the superclass.
super(FigureCanvasAgg, self).draw()
finally:
# if toolbar:
# toolbar.set_cursor(toolbar._lastCursor)
Expand Down
6 changes: 6 additions & 0 deletions lib/matplotlib/backends/backend_qt4cairo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .backend_qt5cairo import _BackendQT5Cairo


@_BackendQT5Cairo.export
class _BackendQT4Cairo(_BackendQT5Cairo):
pass
94 changes: 88 additions & 6 deletions lib/matplotlib/backends/backend_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import signal
import sys
from six import unichr
import traceback

import matplotlib

Expand Down Expand Up @@ -226,19 +227,16 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
# QtCore.Qt.XButton2: None,
}

def _update_figure_dpi(self):
dpi = self._dpi_ratio * self.figure._original_dpi
self.figure._set_dpi(dpi, forward=False)

@_allow_super_init
def __init__(self, figure):
_create_qApp()
super(FigureCanvasQT, self).__init__(figure=figure)

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

self._draw_pending = False
self._is_drawing = False
self._draw_rect_callback = lambda painter: None

self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
self.setMouseTracking(True)
self.resize(*self.get_width_height())
# Key auto-repeat enabled by default
self._keyautorepeat = True

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

def _update_figure_dpi(self):
dpi = self._dpi_ratio * self.figure._original_dpi
self.figure._set_dpi(dpi, forward=False)

@property
def _dpi_ratio(self):
# Not available on Qt4 or some older Qt5.
Expand All @@ -263,6 +271,26 @@ def _dpi_ratio(self):
except AttributeError:
return 1

def _update_dpi(self):
# As described in __init__ above, we need to be careful in cases with
# mixed resolution displays if dpi_ratio is changing between painting
# events.
# Return whether we triggered a resizeEvent (and thus a paintEvent)
# from within this function.
if self._dpi_ratio != self._dpi_ratio_prev:
# We need to update the figure DPI.
self._update_figure_dpi()
self._dpi_ratio_prev = self._dpi_ratio
# The easiest way to resize the canvas is to emit a resizeEvent
# since we implement all the logic for resizing the canvas for
# that event.
event = QtGui.QResizeEvent(self.size(), self.size())
self.resizeEvent(event)
# resizeEvent triggers a paintEvent itself, so we exit this one
# (after making sure that the event is immediately handled).
return True
return False

def get_width_height(self):
w, h = FigureCanvasBase.get_width_height(self)
return int(w / self._dpi_ratio), int(h / self._dpi_ratio)
Expand Down Expand Up @@ -453,6 +481,60 @@ def stop_event_loop(self, event=None):
if hasattr(self, "_event_loop"):
self._event_loop.quit()

def draw(self):
"""Render the figure, and queue a request for a Qt draw.
"""
# The renderer draw is done here; delaying causes problems with code
# that uses the result of the draw() to update plot elements.
if self._is_drawing:
return
self._is_drawing = True
try:
super(FigureCanvasQT, self).draw()
finally:
self._is_drawing = False
self.update()

def draw_idle(self):
"""Queue redraw of the Agg buffer and request Qt paintEvent.
"""
# The Agg draw needs to be handled by the same thread matplotlib
# modifies the scene graph from. Post Agg draw request to the
# current event loop in order to ensure thread affinity and to
# accumulate multiple draw requests from event handling.
# TODO: queued signal connection might be safer than singleShot
if not (self._draw_pending or self._is_drawing):
self._draw_pending = True
QtCore.QTimer.singleShot(0, self._draw_idle)

def _draw_idle(self):
if self.height() < 0 or self.width() < 0:
self._draw_pending = False
if not self._draw_pending:
return
try:
self.draw()
except Exception:
# Uncaught exceptions are fatal for PyQt5, so catch them instead.
traceback.print_exc()
finally:
self._draw_pending = False

def drawRectangle(self, rect):
# Draw the zoom rectangle to the QPainter. _draw_rect_callback needs
# to be called at the end of paintEvent.
if rect is not None:
def _draw_rect_callback(painter):
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
QtCore.Qt.DotLine)
painter.setPen(pen)
painter.drawRect(*(pt / self._dpi_ratio for pt in rect))
else:
def _draw_rect_callback(painter):
return
self._draw_rect_callback = _draw_rect_callback
self.update()


class MainWindow(QtWidgets.QMainWindow):
closing = QtCore.Signal()
Expand Down
129 changes: 14 additions & 115 deletions lib/matplotlib/backends/backend_qt5agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import six

import ctypes
import traceback

from matplotlib import cbook
from matplotlib.transforms import Bbox
Expand All @@ -19,32 +18,11 @@
from .qt_compat import QT_API


class FigureCanvasQTAggBase(FigureCanvasAgg):
"""
The canvas the figure renders into. Calls the draw and print fig
methods, creates the renderers, etc...

Attributes
----------
figure : `matplotlib.figure.Figure`
A high-level Figure instance

"""
class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT):

def __init__(self, figure):
super(FigureCanvasQTAggBase, self).__init__(figure=figure)
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
self._agg_draw_pending = False
self._agg_is_drawing = False
super(FigureCanvasQTAgg, self).__init__(figure=figure)
self._bbox_queue = []
self._drawRect = None

def drawRectangle(self, rect):
if rect is not None:
self._drawRect = [pt / self._dpi_ratio for pt in rect]
else:
self._drawRect = None
self.update()

@property
@cbook.deprecated("2.1")
Expand All @@ -57,27 +35,10 @@ def paintEvent(self, e):
In Qt, all drawing should be done inside of here when a widget is
shown onscreen.
"""
# if there is a pending draw, run it now as we need the updated render
# to paint the widget
if self._agg_draw_pending:
self.__draw_idle_agg()
# As described in __init__ above, we need to be careful in cases with
# mixed resolution displays if dpi_ratio is changing between painting
# events.
if self._dpi_ratio != self._dpi_ratio_prev:
# We need to update the figure DPI
self._update_figure_dpi()
self._dpi_ratio_prev = self._dpi_ratio
# The easiest way to resize the canvas is to emit a resizeEvent
# since we implement all the logic for resizing the canvas for
# that event.
event = QtGui.QResizeEvent(self.size(), self.size())
# We use self.resizeEvent here instead of QApplication.postEvent
# since the latter doesn't guarantee that the event will be emitted
# straight away, and this causes visual delays in the changes.
self.resizeEvent(event)
# resizeEvent triggers a paintEvent itself, so we exit this one.
if self._update_dpi():
# The dpi update triggered its own paintEvent.
return
self._draw_idle() # Only does something if a draw is pending.

# if the canvas does not have a renderer, then give up and wait for
# FigureCanvasAgg.draw(self) to be called
Expand All @@ -100,72 +61,20 @@ def paintEvent(self, e):
reg = self.copy_from_bbox(bbox)
buf = reg.to_string_argb()
qimage = QtGui.QImage(buf, w, h, QtGui.QImage.Format_ARGB32)
# Adjust the buf reference count to work around a memory leak bug
# in QImage under PySide on Python 3.
if QT_API == 'PySide' and six.PY3:
ctypes.c_long.from_address(id(buf)).value = 1
if hasattr(qimage, 'setDevicePixelRatio'):
# Not available on Qt4 or some older Qt5.
qimage.setDevicePixelRatio(self._dpi_ratio)
origin = QtCore.QPoint(l, self.renderer.height - t)
painter.drawImage(origin / self._dpi_ratio, qimage)
# Adjust the buf reference count to work around a memory
# leak bug in QImage under PySide on Python 3.
if QT_API == 'PySide' and six.PY3:
ctypes.c_long.from_address(id(buf)).value = 1

# draw the zoom rectangle to the QPainter
if self._drawRect is not None:
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
QtCore.Qt.DotLine)
painter.setPen(pen)
x, y, w, h = self._drawRect
painter.drawRect(x, y, w, h)
self._draw_rect_callback(painter)

painter.end()

def draw(self):
"""Draw the figure with Agg, and queue a request for a Qt draw.
"""
# The Agg draw is done here; delaying causes problems with code that
# uses the result of the draw() to update plot elements.
if self._agg_is_drawing:
return

self._agg_is_drawing = True
try:
super(FigureCanvasQTAggBase, self).draw()
finally:
self._agg_is_drawing = False
self.update()

def draw_idle(self):
"""Queue redraw of the Agg buffer and request Qt paintEvent.
"""
# The Agg draw needs to be handled by the same thread matplotlib
# modifies the scene graph from. Post Agg draw request to the
# current event loop in order to ensure thread affinity and to
# accumulate multiple draw requests from event handling.
# TODO: queued signal connection might be safer than singleShot
if not (self._agg_draw_pending or self._agg_is_drawing):
self._agg_draw_pending = True
QtCore.QTimer.singleShot(0, self.__draw_idle_agg)

def __draw_idle_agg(self, *args):
# if nothing to do, bail
if not self._agg_draw_pending:
return
# we have now tried this function at least once, do not run
# again until re-armed. Doing this here rather than after
# protects against recursive calls triggered through self.draw
# The recursive call is via `repaintEvent`
self._agg_draw_pending = False
# if negative size, bail
if self.height() < 0 or self.width() < 0:
return
try:
# actually do the drawing
self.draw()
except Exception:
# Uncaught exceptions are fatal for PyQt5, so catch them instead.
traceback.print_exc()

def blit(self, bbox=None):
"""Blit the region in bbox.
"""
Expand All @@ -182,23 +91,13 @@ def blit(self, bbox=None):
self.repaint(l, self.renderer.height / self._dpi_ratio - t, w, h)

def print_figure(self, *args, **kwargs):
super(FigureCanvasQTAggBase, self).print_figure(*args, **kwargs)
super(FigureCanvasQTAgg, self).print_figure(*args, **kwargs)
self.draw()


class FigureCanvasQTAgg(FigureCanvasQTAggBase, FigureCanvasQT):
"""
The canvas the figure renders into. Calls the draw and print fig
methods, creates the renderers, etc.

Modified to import from Qt5 backend for new-style mouse events.

Attributes
----------
figure : `matplotlib.figure.Figure`
A high-level Figure instance

"""
@cbook.deprecated("2.2")
class FigureCanvasQTAggBase(FigureCanvasQTAgg):
pass


@_BackendQT5.export
Expand Down
Loading