浅谈matplotlib中FigureCanvasXAgg的用法


Posted in Python onJune 16, 2020

背景知识:

FigureCanvasXAgg就是一个渲染器,渲染器的工作就是drawing,执行绘图的这个动作。渲染器是使物体显示在屏幕上

主要内容:

将一个figure渲染的canvas变为一个Qt widgets,figure显示的过程是需要管理器(manager),需要FigureCanvasBase来管理。报错信息'FigureCanvasQTAgg' object has no attribute 'manager'

将一个navigation toolbar渲染成Qt widgets

使用用户事件来实时更新matplotlib plot

matplotlib针对GUI设计了两层结构概念:canvas,renderer。

下面我将以默认自带的后端 tkAgg:from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigureCanvas为例,为大家讲解画布与渲染器的知识。

一. canvas(画布)

对应抽象的类:FigureCanvasBase and FigureManagerBase

作用:

保存对图像的引用

更新图像通过对画布的引用

定义运行注册的事件方法

将本地工具箱事件转为matplotlib事件抽象框架

定义绘制渲染图片的方法

停止和开始nono-GUI事件循环

1. 追寻matplotlib.figure.Figure.show( )

以下引自matplotlib.figure.Figure.show( ) 源码和注释:

#matplotlib.figure.Figure.show( )
def show(self, warn=True):
 """
 If using a GUI backend with pyplot, display the figure window.

 If the figure was not created using
 :func:`~matplotlib.pyplot.figure`, it will lack a
 :class:`~matplotlib.backend_bases.FigureManagerBase`, and
 will raise an AttributeError.

 Parameters
 ----------
 warm : bool
  If ``True``, issue warning when called on a non-GUI backend

 Notes
 -----
 For non-GUI backends, this does nothing, in which case a warning will
 be issued if *warn* is ``True`` (default).
 """
 try:
  manager = getattr(self.canvas, 'manager')
 except AttributeError as err:
  raise AttributeError("%s\n"
        "Figure.show works only "
        "for figures managed by pyplot, normally "
        "created by pyplot.figure()." % err)

 if manager is not None:
  try:
   manager.show()
   return
  except NonGuiException:
   pass

它是通过manager.show()来实现画图的动作的。

2. 追寻plt.show()

而在==plt.show( )==的源码中我们可以查到:

#plt.show()
from matplotlib.backends import pylab_setup
_show = pylab_setup()
def show(*args, **kw):

 global _show
 return _show(*args, **kw)

而我们继续查找就得到了,这是在backends包的__init__.py模块里的代码,代码说了一大堆,无非就是说它返回了四个对象:backend_mod, new_figure_manager, draw_if_interactive, show。而show就是show = getattr(backend_mod, 'show', do_nothing_show)得到的其中backend_mod就是要导入模块的绝对路径,之后验证的show就是matplotlib.backends.backend_tkagg._BackendTkAgg,继续追寻之后我们得到class _BackendTkAgg(_BackendTk): FigureCanvas = FigureCanvasTkAgg,之后我们用help函数得到

show(block=None) method of builtins.type instance
 Show all figures.
 
 `show` blocks by calling `mainloop` if *block* is ``True``, or if it
 is ``None`` and we are neither in IPython's ``%pylab`` mode, nor in
 `interactive` mode.

我们继续刨根,寻找从FigureCanvas开始的类的关系和其方法,类的继承结构关系如下图

浅谈matplotlib中FigureCanvasXAgg的用法

然后终于在FigureCnavasTk类的声明中找到了这样的一句声明:

show = cbook.deprecated("2.2", name="FigureCanvasTk.show",
       alternative="FigureCanvasTk.draw")(
        lambda self: self.draw())

也就是说show归根结底是backend里的一个FigureCanvasTk.draw()的一个变形 !

pylab_setup代码如下:

def pylab_setup(name=None):
 '''return new_figure_manager, draw_if_interactive and show for pyplot

 This provides the backend-specific functions that are used by
 pyplot to abstract away the difference between interactive backends.

 Parameters
 ----------
 name : str, optional
  The name of the backend to use. If `None`, falls back to
  ``matplotlib.get_backend()`` (which return :rc:`backend`).

 '''
 # Import the requested backend into a generic module object
 if name is None:
  # validates, to match all_backends
  name = matplotlib.get_backend()
 if name.startswith('module://'):
  backend_name = name[9:]
 else:
  backend_name = 'backend_' + name
  backend_name = backend_name.lower() # until we banish mixed case
  backend_name = 'matplotlib.backends.%s' % backend_name.lower()

 # the last argument is specifies whether to use absolute or relative
 # imports. 0 means only perform absolute imports.
 #得到模块的绝对路径backend_mod,然后通过绝对路径加.就可以调用各个抽象类
 #<module 'matplotlib.backends.backend_tkagg' from 'D:\\Python36\\lib\\site-packages\\matplotlib\\backends\\backend_tkagg.py'>默认实验的!
 backend_mod = __import__(backend_name, globals(), locals(),
        [backend_name], 0)

 # Things we pull in from all backends
 new_figure_manager = backend_mod.new_figure_manager

 # image backends like pdf, agg or svg do not need to do anything
 # for "show" or "draw_if_interactive", so if they are not defined
 # by the backend, just do nothing
 def do_nothing_show(*args, **kwargs):
  frame = inspect.currentframe()
  fname = frame.f_back.f_code.co_filename
  if fname in ('<stdin>', '<ipython console>'):
   warnings.warn("""
Your currently selected backend, '%s' does not support show().
Please select a GUI backend in your matplotlibrc file ('%s')
or with matplotlib.use()""" %
       (name, matplotlib.matplotlib_fname()))

 def do_nothing(*args, **kwargs):
  pass

 backend_version = getattr(backend_mod, 'backend_version', 'unknown')

 show = getattr(backend_mod, 'show', do_nothing_show)

 draw_if_interactive = getattr(backend_mod, 'draw_if_interactive',
         do_nothing)

 _log.debug('backend %s version %s', name, backend_version)

 # need to keep a global reference to the backend for compatibility
 # reasons. See https://github.com/matplotlib/matplotlib/issues/6092
 global backend
 backend = name
 return backend_mod, new_figure_manager, draw_if_interactive, show

3. 追寻plt.figure()

我们创建的这个figure必须有manager,否则则会报错,如果是plt.figure初始化的,plt.figure( )源码如下:

plt.figure()示例

def figure():

 figManager = _pylab_helpers.Gcf.get_fig_manager(num)

 figManager = new_figure_manager(num,figsize=figsize,dpi=dpi,facecolor=facecolor,edgecolor=edgecolor,frameon=frameon,FigureClass=FigureClass,**kwargs)
 ......
 ......
 return figManager.canvas.figure

4. 追寻matplotlib.figure.Figure()

而在matplotlib.figure.Figure() 中,其初始化函数__init__(),并没有默认生成manager这个属性,所以在调用show的时候,就会报错!如上其show函数定义的那样

def __init__(self,
     figsize=None, # defaults to rc figure.figsize
     dpi=None, # defaults to rc figure.dpi
     facecolor=None, # defaults to rc figure.facecolor
     edgecolor=None, # defaults to rc figure.edgecolor
     linewidth=0.0, # the default linewidth of the frame
     frameon=None, # whether or not to draw the figure frame
     subplotpars=None, # default to rc
     tight_layout=None, # default to rc figure.autolayout
     constrained_layout=None, # default to rc
           #figure.constrained_layout.use
     ):
  """
  Parameters
  ----------
  figsize : 2-tuple of floats
   ``(width, height)`` tuple in inches

  dpi : float
   Dots per inch

  facecolor
   The figure patch facecolor; defaults to rc ``figure.facecolor``

  edgecolor
   The figure patch edge color; defaults to rc ``figure.edgecolor``

  linewidth : float
   The figure patch edge linewidth; the default linewidth of the frame

  frameon : bool
   If ``False``, suppress drawing the figure frame

  subplotpars : :class:`SubplotParams`
   Subplot parameters, defaults to rc

  tight_layout : bool
   If ``False`` use *subplotpars*; if ``True`` adjust subplot
   parameters using `.tight_layout` with default padding.
   When providing a dict containing the keys
   ``pad``, ``w_pad``, ``h_pad``, and ``rect``, the default
   `.tight_layout` paddings will be overridden.
   Defaults to rc ``figure.autolayout``.

  constrained_layout : bool
   If ``True`` use constrained layout to adjust positioning of plot
   elements. Like ``tight_layout``, but designed to be more
   flexible. See
   :doc:`/tutorials/intermediate/constrainedlayout_guide`
   for examples. (Note: does not work with :meth:`.subplot` or
   :meth:`.subplot2grid`.)
   Defaults to rc ``figure.constrained_layout.use``.
  """
  Artist.__init__(self)
  # remove the non-figure artist _axes property
  # as it makes no sense for a figure to be _in_ an axes
  # this is used by the property methods in the artist base class
  # which are over-ridden in this class
  del self._axes
  self.callbacks = cbook.CallbackRegistry()

  if figsize is None:
   figsize = rcParams['figure.figsize']
  if dpi is None:
   dpi = rcParams['figure.dpi']
  if facecolor is None:
   facecolor = rcParams['figure.facecolor']
  if edgecolor is None:
   edgecolor = rcParams['figure.edgecolor']
  if frameon is None:
   frameon = rcParams['figure.frameon']

  if not np.isfinite(figsize).all():
   raise ValueError('figure size must be finite not '
        '{}'.format(figsize))
  self.bbox_inches = Bbox.from_bounds(0, 0, *figsize)

  self.dpi_scale_trans = Affine2D().scale(dpi, dpi)
  # do not use property as it will trigger
  self._dpi = dpi
  self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans)

  self.frameon = frameon

  self.transFigure = BboxTransformTo(self.bbox)

  self.patch = Rectangle(
   xy=(0, 0), width=1, height=1,
   facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth)
  self._set_artist_props(self.patch)
  self.patch.set_aa(False)

  self._hold = rcParams['axes.hold']
  if self._hold is None:
   self._hold = True

  self.canvas = None
  self._suptitle = None

  if subplotpars is None:
   subplotpars = SubplotParams()

  self.subplotpars = subplotpars
  # constrained_layout:
  self._layoutbox = None
  # set in set_constrained_layout_pads()
  self.set_constrained_layout(constrained_layout)

  self.set_tight_layout(tight_layout)

  self._axstack = AxesStack() # track all figure axes and current axes
  self.clf()
  self._cachedRenderer = None

  # groupers to keep track of x and y labels we want to align.
  # see self.align_xlabels and self.align_ylabels and
  # axis._get_tick_boxes_siblings
  self._align_xlabel_grp = cbook.Grouper()
  self._align_ylabel_grp = cbook.Grouper()

综上所述,我们通过matplotlib.figure.Figure()来创建得到的fig,并不具备manager的属性,而通过plt.figure()创建的fig,就默认创建了manager。

二 . renderer(渲染器),默认是tkagg

对应抽象的类:RendererBase and GraphicsContextBase

作用:

- 很多渲染操作都传递给一个额外的抽象:GraphicsContextBase,它为处理颜色、线条样式、起始样式、混合属性和反混叠选项等的代码提供了一个干净的分离。

Qt & matplotlib示例代码

#import modules from Matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
import matplotlib.pyplot as plt
#import random module to generate set
import random

class Window(QtGui.QDialog):
	def __init__(self, parent=None):
		super(Window, self).__init__(parent)
		#init figure and canvas
		self.figure = plt.figure()
		self.canvas = FigureCanvas(self.figure)
		#init nav toolbar
		self.toolbar = NavigationToolbar(self.canvas, self)
		# Add plot button
		self.button = QtGui.QPushButton('Plot')
		# connect button to custom slot (see later)
		self.button.clicked.connect(self.plot)
		# set the layout
		layout = QtGui.QVBoxLayout()
		layout.addWidget(self.toolbar)
		layout.addWidget(self.canvas)
		layout.addWidget(self.button)
		self.setLayout(layout)
	### our custom slot
	def plot(self):
		# random data
		data = [random.random() for i in range(25)]
		# create an axis
		ax = self.figure.add_subplot(1,1,1)
		# discards the old graph
		ax.hold(False)
		# plot data
		ax.plot(data, '*­')
		# refresh canvas
		self.canvas.draw()

三. Problems(GUI画3D不能旋转)

一个Axes3D创建callback函数给画布上的图形实现旋转特性。如果说先给图形(figure)增加axes或者其他配件的时候,在之后将图形附加到画布的时候,之前添加的axes的callback函数可能不能够接收消息事件,也就没办法在绘出的GUI实现旋转的性能。

所以应该先将图形附加到画布上,然后再对图形增加axes和其他的配件。

FigureCanvas(figure,canvas)

figure:需要附加的图形(添加者),canvas提供渲染功能的对象(承载者)

每一次你调用FigureCanvas()的时候,你都是将图形附加到新画布上(这不是你所看到的的那个canvas),于是 the call-backs函数将不会被射击(接收事件信号),因为他们正在监听一个你看不到的canvas。

四 . 附录

浅谈matplotlib中FigureCanvasXAgg的用法

以上这篇浅谈matplotlib中FigureCanvasXAgg的用法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python 文件操作实现代码
Oct 07 Python
在Python的struct模块中进行数据格式转换的方法
Jun 17 Python
Python实现字典的key和values的交换
Aug 04 Python
基于Python Shell获取hostname和fqdn释疑
Jan 25 Python
Python实现字典去除重复的方法示例
Jul 31 Python
Selenium的使用详解
Oct 19 Python
如何利用Python分析出微信朋友男女统计图
Jan 25 Python
python sorted方法和列表使用解析
Nov 18 Python
使用 Python 遍历目录树的方法
Feb 29 Python
详解appium自动化测试工具(monitor、uiautomatorviewer)
Jan 27 Python
Flask中jinja2的继承实现方法及实例
Mar 03 Python
Pygame Draw绘图函数的具体使用
Nov 17 Python
利用Python实现Excel的文件间的数据匹配功能
Jun 16 #Python
Pytorch 使用CNN图像分类的实现
Jun 16 #Python
利用python中的matplotlib打印混淆矩阵实例
Jun 16 #Python
Python SMTP配置参数并发送邮件
Jun 16 #Python
基于matplotlib中ion()和ioff()的使用详解
Jun 16 #Python
Python数据相关系数矩阵和热力图轻松实现教程
Jun 16 #Python
matplotlib.pyplot.matshow 矩阵可视化实例
Jun 16 #Python
You might like
建立文件交换功能的脚本(二)
2006/10/09 PHP
采集邮箱的php代码(抓取网页中的邮箱地址)
2012/07/17 PHP
360通用php防护代码(使用操作详解)
2013/06/18 PHP
PHP回溯法解决0-1背包问题实例分析
2015/03/23 PHP
laravel5环境隐藏index.php后缀(apache)的方法
2019/10/12 PHP
jsonp原理及使用
2013/10/28 Javascript
jQuery表格排序组件-tablesorter使用示例
2014/05/26 Javascript
使用delegate方法为一个tr标签加一个链接
2014/06/27 Javascript
javascript使用正则表达式检测IP地址
2014/12/03 Javascript
深入探究使JavaScript动画流畅的一些方法
2015/06/30 Javascript
微信小程序 canvas API详解及实例代码
2016/10/08 Javascript
jQuery Validate 校验多个相同name的方法
2017/05/18 jQuery
详解为Bootstrap Modal添加拖拽的方法
2018/01/05 Javascript
仿iPhone通讯录制作小程序自定义选择组件的实现
2019/05/23 Javascript
Openlayers实现点闪烁扩散效果
2020/09/24 Javascript
[01:15:00]LGD vs Mineski Supermajor 胜者组 BO3 第一场 6.5
2018/06/06 DOTA
Python常用内置函数总结
2015/02/08 Python
python用来获得图片exif信息的库实例分析
2015/03/16 Python
详解Python中for循环的使用
2015/04/14 Python
Python开发的HTTP库requests详解
2017/08/29 Python
django解决跨域请求的问题详解
2019/01/20 Python
Python多线程处理实例详解【单进程/多进程】
2019/01/30 Python
Pandas聚合运算和分组运算的实现示例
2019/10/17 Python
解决jupyter notebook import error但是命令提示符import正常的问题
2020/04/15 Python
python+selenium爬取微博热搜存入Mysql的实现方法
2021/01/27 Python
基于tensorflow __init__、build 和call的使用小结
2021/02/26 Python
Html5 实现微信分享及自定义内容的流程
2019/08/20 HTML / CSS
用HTML5.0制作网页的教程
2010/05/30 HTML / CSS
荷兰度假屋租赁网站:Aan Zee
2020/02/28 全球购物
如何写你的创业计划书
2014/01/07 职场文书
优秀导游先进事迹材料
2014/01/25 职场文书
2014年作风建设剖析材料
2014/10/23 职场文书
2015年社区平安建设工作总结
2015/05/13 职场文书
食品安全主题班会
2015/08/13 职场文书
详解Spring Boot使用系统参数表提升系统的灵活性
2021/06/30 Java/Android
悬疑名作《朋友游戏》动画无字ED宣传片 新角色公开
2022/04/13 日漫