为什么matplotlib.figure.Figure的行为与matplotlib.pyplot.figure

时间:2015-07-24 12:59:32

标签: python matplotlib tkinter

同事程序员提醒我一个matplotlib.pyplot和Tkinter不能很好地协同工作的问题,正如这个问题Tkinter/Matplotlib backend conflict causes infinite mainloop所示

我们更改了代码以防止链接问题中提到的潜在问题,如下所示:

import matplotlib.pyplot as plt
self.fig = plt.figure(figsize=(8,6))
if os.path.isfile('./UI.png'):
    image = plt.imread('./UI.png')
    plt.axis('off')
    plt.tight_layout()
    im = plt.imshow(image)
# The Canvas
self.canvas = FigureCanvasTkAgg(self.fig, master = master)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, root)
self.canvas.get_tk_widget().pack(fill=BOTH,expand=YES)
self.canvas.draw()

中级(未显示UI.png)

import matplotlib.pyplot as plt
import matplotlib
self.fig = matplotlib.figure.Figure(figsize=(8, 6))
if os.path.isfile('./UI.png'):
    image = matplotlib.image.imread('./UI.png')
    plt.axis('off')
    plt.tight_layout()
    plt.imshow(image)
# The Canvas
self.canvas = FigureCanvasTkAgg(self.fig, master=master)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, root)
self.canvas.get_tk_widget().pack(fill=BOTH, expand=YES)
self.canvas.draw()

更改的代码未显示'背景'图像了,我一直只是尝试随机的东西(因为我在两个选项之间的差异相当迷失)才能再次显示图形。如https://github.com/matplotlib/matplotlib/issues/1852所述,更改涉及从tight_layout切换到set_tight_layout以避免出现警告。结果代码如下:

潜在修复

import matplotlib.pyplot as plt
import matplotlib
self.fig = matplotlib.figure.Figure(figsize=(8, 6))
background_image = self.fig.add_subplot(111)
if os.path.isfile('./UI.png'):
    image = matplotlib.image.imread('./UI.png')
    background_image.axis('off')
    #self.fig.tight_layout() # This throws a warning and falls back to Agg renderer, 'avoided' by using the line below this one.
    self.fig.set_tight_layout(True)
    background_image.imshow(image)
# The Canvas
self.canvas = FigureCanvasTkAgg(self.fig, master=master)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, root)
self.canvas.get_tk_widget().pack(fill=BOTH, expand=YES)
self.canvas.draw()

因此,问题是,为什么我们现在需要使用子图(使用matplotlib.figure.Figure)而我们之前没有(使用matplotlib.pyplot)?

PS:如果这是一个愚蠢的问题,我很抱歉,但我在这个问题上找到的几乎所有内容似乎都使用了matplotlib.pyplot变体。因此,我无法找到matplotlib.figure.Figure变种的任何好文档。

1 个答案:

答案 0 :(得分:2)

TL; DR

  

因此,问题是,为什么我们现在需要使用子图(使用matplotlib.figure.Figure)而我们之前没有(使用matplotlib.pyplot)?

subplot会创建一个Axes对象。你之前确实有一个,但是pyplot API" hid"它来自你的封面所以你没有意识到它。您现在正在尝试直接使用这些对象,因此必须自己处理它。

更详细的原因

您看到此行为的原因是matplotlib.pyplot的工作原理。引用the tutorial一点:

  

matplotlib.pyplot是命令样式函数的集合,它使matplotlib像MATLAB一样工作.... matplotlib.pyplot是有状态的,因为它跟踪当前的图形和绘图区域,以及绘图功能被引导到当前轴

关键是pyplot是有状态的。它正在跟踪状态"在封面下#34;并在某种程度上隐藏对象模型。它也做了一些隐含的事情。所以 - 如果你只是打电话,例如,plt.axis(),在pyplot calls plt.gca()下,而calls gcf()依次打return a new figure,因为你还没有设置一个通过pyplot来计算。对于plt.some_function()的大多数来电都是如此 - 如果pyplot没有数字对象 在其状态 然而,它会创造一个。

因此,在您的中间示例中,您已经创建了自己的Figure对象 - 其名称为self.fig(我不确定您的班级结构是什么,所以我我不知道self是什么,但我猜测它是你的tk.Frame对象或类似东西。

妙语

pyplot self.fig 一无所知。因此,在您的中间代码中,您在imshow()状态的Figure对象上调用pyplot,但在画布上显示不同的数字(self.fig)。 / p>

问题不在于您需要使用subplot,而是需要更改正确的Figure对象上的背景图像。您在潜在修订代码中使用subplot的方式会做到这一点 - 尽管我建议在下面使用替代方案可能会使意图更清晰。

如何修复

更改

plt.axis('off')
plt.tight_layout()
plt.imshow(image)

self.fig.set_tight_layout(True)
ax = self.fig.gca() # You could use subplot here to get an Axes object instead
ax.axis('off')
ax.imshow(image)

关于根本原因的说明:pyplot API与直接使用对象

这有点意见,但可能有所帮助。当我需要快速获取原型并希望使用其中一个相当标准的案例时,我倾向于使用pyplot接口。通常,这就足够了。

一旦我需要做更复杂的事情,我就开始直接使用对象模型 - 维护我自己的名为FigureAxes对象等。

混合两者是可能的,但经常令人困惑。您已经通过中间解决方案找到了这个。所以我建议做一个或另一个。