python - 防止 matplotlib 有状态

标签 python matplotlib

如果我在 matplotlib 中创建一个 Axes 对象并改变它(即通过绘制一些数据)然后我调用一个函数 而不传递我的 Axes 对象指向该函数 那么该函数仍然可以改变我的 Axes。例如:

import matplotlib.pyplot as plt
import numpy as np

def innocent_looking_function():
    #let's draw a red line on some unsuspecting Axes!
    plt.plot(100*np.random.rand(20), color='r')

fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20), color='b') #draw blue line on ax
#ax now has a blue line, as expected

innocent_looking_function()
#ax now unexpectedly has a blue line and a red line!

我的问题是:我能否在一般情况下阻止这种全局变量行为?我知道我可以在调用任何 innocent_looking_function() 之前调用 plt.close() 但有什么方法可以将其设置为默认值吗?

最佳答案

当然!您需要做的是在制作图形时完全绕过 pyplot 状态机。

它更冗长,因为您不能只调用 fig = plt.figure()


首先,让我解释一下 plt.gca()plt.gcf() 是如何工作的。使用 pyplot 接口(interface)时,matplotlib 存储所有已创建但未显示的图形管理器。图管理器基本上是图的图形用户界面包装器。

plt._pylab_helpers.Gcf 是存储图形管理器并跟踪当前处于事件状态的单例对象。 plt.gcf()_pylab_helpers.Gcf 返回事件图形。每个 Figure 对象都跟踪它自己的轴,所以 plt.gca() 就是 plt.gcf().gca()

通常,当您调用 plt.figure() 时,它:

  1. 创建返回的图形对象
  2. 使用适当的后端为该图形创建一个 FigureManager
  3. 图形管理器创建一个 FigureCanvas、gui 窗口(根据需要)和 NavigationToolbar2(缩放按钮等)
  4. 图形管理器实例随后被添加到 _pylab_helpers.Gcf 的图形列表中。

这是我们要绕过的最后一步。


这是一个使用非交互式后端的简单示例。请注意,因为我们不担心与绘图交互,所以我们可以跳过整个图形管理器,只创建一个 FigureFigureCanvas 实例。 (从技术上讲,我们可以跳过 FigureCanvas,但只要我们想将绘图保存到图像等,就需要它)

import matplotlib.backends.backend_agg as backend
from matplotlib.figure import Figure

# The pylab figure manager will be bypassed in this instance. `plt.gca()`
# can't access the axes created here.
fig = Figure()
canvas = backend.FigureCanvas(fig)
ax = fig.add_subplot(111)

只是为了证明 gca 看不到这个轴:

import matplotlib.pyplot as plt
import matplotlib.backends.backend_agg as backend
from matplotlib.figure import Figure

# Independent figure/axes
fig = Figure()
canvas = backend.FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.plot(range(10))

# gca() is completely unaware of this axes and will create a new one instead:
ax2 = plt.gca()
print 'Same axes?:', id(ax) == id(ax2)

# And `plt.show()` would show the blank axes of `ax2`

有了交互式支持,它会更复杂一些。你不能调用 plt.show(),所以你需要自己启动 gui 的主循环。您可以“从头开始”完成这一切(请参阅任何“嵌入 matplotlib”示例),但 FigureManager 将支持的特定部分抽象掉:

作为使用 TkAgg 后端的示例:

import matplotlib.backends.backend_tkagg as backend
from matplotlib.figure import Figure

fig = Figure()
ax = fig.add_subplot(111)

manager = backend.new_figure_manager_given_figure(1, fig)
manager.show()
backend.show.mainloop()

要使用其他后端之一,只需更改后端导入即可。例如,对于 Qt4:

import matplotlib.backends.backend_qt4agg as backend
from matplotlib.figure import Figure

fig = Figure()
ax = fig.add_subplot(111)

manager = backend.new_figure_manager_given_figure(1, fig)
manager.show()
backend.show.mainloop()

这实际上甚至适用于 IPython 笔记本中使用的 nbagg 后端。只需将后端导入更改为 import matplotlib.backends.backend_nbagg as backend

关于python - 防止 matplotlib 有状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28631741/

相关文章:

python - 在 Python 中使用字典作为集合中的项目

python - 如何在 Python 中获取函数名称作为字符串?

Python在两个引号之间查找以特定单词开头的行

python - matplotlib 图形在轴上方和下方填充 2 种颜色

python - 带日期的 matplotlib

python - 如何将 Pandas 数据框字符串更改为整数?

python - 如何使用 Matplotlib 缩放体素尺寸?

python - Matplotlib将某些字符绘制为空白正方形

python - 如何获取颜色图的一部分

python - 运行时警告 : invalid value encountered in arccos