backend_qt5agg.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. """
  2. Render to qt from agg.
  3. """
  4. import ctypes
  5. from matplotlib.transforms import Bbox
  6. from .. import cbook
  7. from .backend_agg import FigureCanvasAgg
  8. from .backend_qt5 import (
  9. QtCore, QtGui, QtWidgets, _BackendQT5, FigureCanvasQT, FigureManagerQT,
  10. NavigationToolbar2QT, backend_version)
  11. from .qt_compat import QT_API, _setDevicePixelRatioF
  12. class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT):
  13. def __init__(self, figure):
  14. # Must pass 'figure' as kwarg to Qt base class.
  15. super().__init__(figure=figure)
  16. def paintEvent(self, event):
  17. """
  18. Copy the image from the Agg canvas to the qt.drawable.
  19. In Qt, all drawing should be done inside of here when a widget is
  20. shown onscreen.
  21. """
  22. if self._update_dpi():
  23. # The dpi update triggered its own paintEvent.
  24. return
  25. self._draw_idle() # Only does something if a draw is pending.
  26. # If the canvas does not have a renderer, then give up and wait for
  27. # FigureCanvasAgg.draw(self) to be called.
  28. if not hasattr(self, 'renderer'):
  29. return
  30. painter = QtGui.QPainter(self)
  31. try:
  32. # See documentation of QRect: bottom() and right() are off
  33. # by 1, so use left() + width() and top() + height().
  34. rect = event.rect()
  35. # scale rect dimensions using the screen dpi ratio to get
  36. # correct values for the Figure coordinates (rather than
  37. # QT5's coords)
  38. width = rect.width() * self._dpi_ratio
  39. height = rect.height() * self._dpi_ratio
  40. left, top = self.mouseEventCoords(rect.topLeft())
  41. # shift the "top" by the height of the image to get the
  42. # correct corner for our coordinate system
  43. bottom = top - height
  44. # same with the right side of the image
  45. right = left + width
  46. # create a buffer using the image bounding box
  47. bbox = Bbox([[left, bottom], [right, top]])
  48. reg = self.copy_from_bbox(bbox)
  49. buf = cbook._unmultiplied_rgba8888_to_premultiplied_argb32(
  50. memoryview(reg))
  51. # clear the widget canvas
  52. painter.eraseRect(rect)
  53. qimage = QtGui.QImage(buf, buf.shape[1], buf.shape[0],
  54. QtGui.QImage.Format_ARGB32_Premultiplied)
  55. _setDevicePixelRatioF(qimage, self._dpi_ratio)
  56. # set origin using original QT coordinates
  57. origin = QtCore.QPoint(rect.left(), rect.top())
  58. painter.drawImage(origin, qimage)
  59. # Adjust the buf reference count to work around a memory
  60. # leak bug in QImage under PySide on Python 3.
  61. if QT_API in ('PySide', 'PySide2'):
  62. ctypes.c_long.from_address(id(buf)).value = 1
  63. self._draw_rect_callback(painter)
  64. finally:
  65. painter.end()
  66. def print_figure(self, *args, **kwargs):
  67. super().print_figure(*args, **kwargs)
  68. self.draw()
  69. @_BackendQT5.export
  70. class _BackendQT5Agg(_BackendQT5):
  71. FigureCanvas = FigureCanvasQTAgg