淺談matplotlib中FigureCanvasXAgg的用法
背景知識:
FigureCanvasXAgg就是一個渲染器,渲染器的工作就是drawing,執(zhí)行繪圖的這個動作。渲染器是使物體顯示在屏幕上
主要內容:
將一個figure渲染的canvas變?yōu)橐粋€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事件循環(huán)
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()來實現(xiàn)畫圖的動作的。
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)
而我們繼續(xù)查找就得到了,這是在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,繼續(xù)追尋之后我們得到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.
我們繼續(xù)刨根,尋找從FigureCanvas開始的類的關系和其方法,類的繼承結構關系如下圖

然后終于在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()
我們創(chuàng)建的這個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()來創(chuàng)建得到的fig,并不具備manager的屬性,而通過plt.figure()創(chuàng)建的fig,就默認創(chuàng)建了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創(chuàng)建callback函數給畫布上的圖形實現(xiàn)旋轉特性。如果說先給圖形(figure)增加axes或者其他配件的時候,在之后將圖形附加到畫布的時候,之前添加的axes的callback函數可能不能夠接收消息事件,也就沒辦法在繪出的GUI實現(xiàn)旋轉的性能。
所以應該先將圖形附加到畫布上,然后再對圖形增加axes和其他的配件。
FigureCanvas(figure,canvas)
figure:需要附加的圖形(添加者),canvas提供渲染功能的對象(承載者)
每一次你調用FigureCanvas()的時候,你都是將圖形附加到新畫布上(這不是你所看到的的那個canvas),于是 the call-backs函數將不會被射擊(接收事件信號),因為他們正在監(jiān)聽一個你看不到的canvas。
四 . 附錄

以上這篇淺談matplotlib中FigureCanvasXAgg的用法就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
跟老齊學Python之集成開發(fā)環(huán)境(IDE)
IDE的全稱是:Integrated Development Environment,簡稱IDE,也稱為Integration Design Environment、Integration Debugging Environment,翻譯成中文叫做“集成開發(fā)環(huán)境”,在臺灣那邊叫做“整合開發(fā)環(huán)境”。2014-09-09
Python requests HTTP驗證登錄實現(xiàn)流程
這篇文章主要介紹了Python requests HTTP驗證登錄實現(xiàn)流程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-11-11

