浅谈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下的Mysql模块MySQLdb安装详解
Apr 09 Python
Python实现扫描指定目录下的子目录及文件的方法
Jul 16 Python
使用Python的Flask框架表单插件Flask-WTF实现Web登录验证
Jul 12 Python
解决安装tensorflow遇到无法卸载numpy 1.8.0rc1的问题
Jun 13 Python
Python文件常见操作实例分析【读写、遍历】
Dec 10 Python
Django框架静态文件使用/中间件/禁用ip功能实例详解
Jul 22 Python
Python+Redis实现布隆过滤器
Dec 08 Python
Python Numpy库常见用法入门教程
Jan 16 Python
解决flask接口返回的内容中文乱码的问题
Apr 03 Python
python实现交并比IOU教程
Apr 16 Python
python des,aes,rsa加解密的实现
Jan 16 Python
python 实现mysql自动增删分区的方法
Apr 01 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
PHP获取网卡地址的代码
2008/04/09 PHP
[原创]php token使用与验证示例【测试可用】
2017/08/30 PHP
脚本吧 - 幻宇工作室用到js,超强推荐share.js
2006/12/23 Javascript
jquery自动完成插件(autocomplete)应用之PHP版
2009/12/15 Javascript
json原理分析及实例介绍
2012/11/29 Javascript
文档对象模型DOM通俗讲解
2013/11/01 Javascript
js中window.open打开一个新的页面
2014/08/10 Javascript
JavaScript中创建对象的7种模式详解
2017/02/21 Javascript
Javascript中类式继承和原型式继承的实现方法和区别之处
2017/04/25 Javascript
修改Nodejs内置的npm默认配置路径方法
2018/05/13 NodeJs
Vue.directive 实现元素scroll逻辑复用
2019/11/29 Javascript
jquery实现进度条状态展示
2020/03/26 jQuery
jQuery 实现DOM元素拖拽交换位置的实例代码
2020/07/14 jQuery
Nuxt 嵌套路由nuxt-child组件用法(父子页面组件的传值)
2020/11/05 Javascript
python进阶教程之函数对象(函数也是对象)
2014/08/30 Python
详解duck typing鸭子类型程序设计与Python的实现示例
2016/06/03 Python
Python基于whois模块简单识别网站域名及所有者的方法
2018/04/23 Python
朴素贝叶斯分类算法原理与Python实现与使用方法案例
2018/06/26 Python
python 实现语音聊天机器人的示例代码
2018/12/02 Python
python中数组和矩阵乘法及使用总结(推荐)
2019/05/18 Python
解决django服务器重启端口被占用的问题
2019/07/26 Python
Python list运算操作代码实例解析
2020/01/20 Python
python读取mysql数据绘制条形图
2020/03/25 Python
Keras 切换后端方式(Theano和TensorFlow)
2020/06/19 Python
10个很棒的 CSS3 开发工具 推荐
2011/05/16 HTML / CSS
全球知名旅游社区法国站点:TripAdvisor法国
2016/08/03 全球购物
Spartoo美国:欧洲排名第一的在线时装零售商
2019/12/12 全球购物
C#实现对任一张表的数据进行增,删,改,查要求,运用Webservice,体现出三层架构
2014/07/11 面试题
C#中有没有运算符重载?能否使用指针?
2014/05/05 面试题
计算机软件个人的自荐信范文
2013/12/01 职场文书
《搭石》教学反思
2014/04/07 职场文书
科学发展观演讲稿
2014/09/11 职场文书
撤诉状格式范本
2015/05/19 职场文书
靠谱的活动总结
2019/04/16 职场文书
Golang MatrixOne使用介绍和汇编语法
2022/04/19 Golang
python turtle绘图
2022/05/04 Python