浅谈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类和函数中使用静态变量的方法
May 09 Python
python 远程统计文件代码分享
May 14 Python
Python的randrange()方法使用教程
May 15 Python
Python随机数random模块使用指南
Sep 09 Python
用不到50行的Python代码构建最小的区块链
Nov 16 Python
python交互式图形编程实例(一)
Nov 17 Python
Django使用中间键实现csrf认证详解
Jul 22 Python
Django处理Ajax发送的Get请求代码详解
Jul 29 Python
python os.fork() 循环输出方法
Aug 08 Python
如何让python的运行速度得到提升
Jul 08 Python
Vs Code中8个好用的python 扩展插件
Oct 12 Python
一文带你掌握Pyecharts地理数据可视化的方法
Feb 06 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
用Apache反向代理设置对外的WWW和文件服务器
2006/10/09 PHP
基于php常用函数总结(数组,字符串,时间,文件操作)
2013/06/27 PHP
php实现的替换敏感字符串类实例
2014/09/22 PHP
php实现的数字验证码及数字运算验证码
2015/07/30 PHP
PHP预定义变量9大超全局数组用法详解
2016/04/23 PHP
javascript编程起步(第七课)
2007/01/10 Javascript
利用javascript中的call实现继承
2007/01/22 Javascript
基于Jquery插件开发之图片放大镜效果(仿淘宝)
2011/11/19 Javascript
JavaScript中使用stopPropagation函数停止事件传播例子
2014/08/27 Javascript
使用ajax+jqtransform实现动态加载select
2014/12/01 Javascript
详解JavaScript的while循环的使用
2015/06/03 Javascript
JavaScript实现的圆形浮动标签云效果实例
2015/08/06 Javascript
ajax如何实现页面局部跳转与结果返回
2015/08/24 Javascript
AngularJS监听路由的变化示例代码
2016/09/23 Javascript
微信小程序 es6-promise.js封装请求与处理异步进程
2017/06/12 Javascript
Js判断H5上下滑动方向及滑动到顶部和底部判断的示例代码
2017/11/15 Javascript
JavaScript多线程运行库Nexus.js详解
2017/12/22 Javascript
vue-cli3.0配置及使用注意事项详解
2018/09/05 Javascript
详解JQuery基础动画操作
2019/04/12 jQuery
[40:50]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS LGD第四场
2014/05/24 DOTA
[05:03]2018DOTA2亚洲邀请赛主赛事首日回顾
2018/04/04 DOTA
python实现的各种排序算法代码
2013/03/04 Python
Python中使用Boolean操作符做真值测试实例
2015/01/30 Python
Python实现比较两个文件夹中代码变化的方法
2015/07/10 Python
对Tensorflow中的矩阵运算函数详解
2018/07/27 Python
html5+css3之动画在webapp中的应用
2014/11/21 HTML / CSS
html5贪吃蛇游戏使用63行代码完美实现
2013/06/25 HTML / CSS
基督教卡片、励志礼品、家居装饰等:DaySpring
2018/10/12 全球购物
英国天然宝石首饰购买网站:Gemondo Jewellery
2018/10/23 全球购物
设计模式的基本要素是什么
2014/04/21 面试题
初三学生评语大全
2014/04/24 职场文书
公益广告标语
2014/06/19 职场文书
人事聘任通知
2015/04/21 职场文书
聘任通知书
2015/09/21 职场文书
公司趣味运动会开幕词
2016/03/04 职场文书
2016年妇联“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书