[英]Why does matplotlib.figure.Figure behave so different than matplotlib.pyplot.figure
A fellow programmer alerted me to a problem where matplotlib.pyplot and Tkinter don't behave well together, as demonstrated by this question Tkinter/Matplotlib backend conflict causes infinite mainloop 一位同事程序员提醒我一个matplotlib.pyplot和Tkinter不能很好地协同工作的问题,正如这个问题所证明的那样Tkinter / Matplotlib后端冲突导致无限主循环
We changed our code to prevent potential problems as mentioned in the linked question, as follows: 我们更改了代码以防止链接问题中提到的潜在问题,如下所示:
Old 旧
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()
Intermediate (UI.png not being shown) 中级(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()
The changed code did not display the 'background' image anymore and I have been mostly just trying random things (as I am quite lost in the difference between the two options) to get the figure displaying again. 改变后的代码不再显示“背景”图像,而且我大多只是尝试随机的事情(因为我在两个选项之间的差异中完全迷失)才能再次显示图形。 The changes involved switching from tight_layout
to set_tight_layout
to avoid a warning, as mentioned on https://github.com/matplotlib/matplotlib/issues/1852 . 这些更改涉及从tight_layout
切换到set_tight_layout
以避免警告,如https://github.com/matplotlib/matplotlib/issues/1852所述 。 The resulting code is as follows: 结果代码如下:
Potential Fix 潜在的修复
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()
The question therefore is, why do we need to use a subplot now (using matplotlib.figure.Figure) while before we did not (using matplotlib.pyplot)? 因此,问题是,为什么我们现在需要使用子图(使用matplotlib.figure.Figure)而不是之前(使用matplotlib.pyplot)?
PS: I am sorry if this is a silly question but almost everything that I can find on the subject seems to use the matplotlib.pyplot
variant. PS:我很抱歉,如果这是一个愚蠢的问题,但几乎我在这个问题上找到的所有内容似乎都使用了matplotlib.pyplot
变体。 Therefore, I am having trouble finding any good documentation for the matplotlib.figure.Figure
variant. 因此,我无法找到matplotlib.figure.Figure
变体的任何好文档。
The question therefore is, why do we need to use a subplot now (using matplotlib.figure.Figure) while before we did not (using matplotlib.pyplot)? 因此,问题是,为什么我们现在需要使用子图(使用matplotlib.figure.Figure)而不是之前(使用matplotlib.pyplot)?
subplot
creates an Axes
object. subplot
创建一个Axes
对象。 You did have one before, but the pyplot
API "hid" it from you under the covers so you didn't realise it. 你之前确实有过一个,但是pyplot
API“隐藏”了你的封面所以你没有意识到它。 You are now trying to use the objects directly, so have to handle it yourself. 您现在正在尝试直接使用这些对象,因此必须自己处理它。
The reason you see this behaviour is because of how matplotlib.pyplot
works. 您看到此行为的原因是因为matplotlib.pyplot
工作原理。 To quote the tutorial a little: 引用教程一点:
matplotlib.pyplot
is a collection of command style functions that make matplotlib work like MATLAB....matplotlib.pyplot
is stateful, in that it keeps track of the current figure and plotting area, and the plotting functions are directed to the current axesmatplotlib.pyplot
是命令样式函数的集合,它使matplotlib像MATLAB一样工作....matplotlib.pyplot
是有状态的,因为它跟踪当前的图形和绘图区域,绘图函数指向当前轴
The crucial bit is that pyplot
is stateful. 关键是pyplot
是有状态的。 It is keeping track of state "under the covers" and hiding the object model from you to some extent. 它正在跟踪状态“隐藏”并在某种程度上隐藏对象模型。 It also does some implicit things. 它也做了一些隐含的事情。 So - if you simply call, eg, plt.axis()
, under the covers pyplot
calls plt.gca()
and that in turn calls gcf()
which will return a new figure , because you haven't set up a figure via pyplot
yet. 所以 - 如果你只是调用,例如, plt.axis()
,在封面下pyplot
调用plt.gca()
,然后调用gcf()
,这将返回一个新的数字 ,因为你还没有设置一个数字pyplot
呢。 This is true for most calls to plt.some_function()
- if pyplot
doesn't have a figure object in it's own state yet, it will create one. 大多数对plt.some_function()
调用都是如此 - 如果pyplot
在它自己的状态中没有一个figure对象,它将创建一个。
So, in your intermediate example, you've created your own Figure
object - fiven it a name self.fig
(I'm not sure what your class structure is, so I don't know what self
is, but I'm guessing it's your tk.Frame
object or something similar). 所以,在你的中间例子中,你已经创建了自己的Figure
对象 - 这就是一个名字self.fig
(我不确定你的类结构是什么,所以我不知道self
是什么,但我猜它是你的tk.Frame
对象或类似的东西)。
pyplot
doesn't know anything about self.fig
. pyplot
对self.fig
。 So in your intermediate code, you're calling imshow()
on the Figure
object in pyplot
state, but displaying a different figure ( self.fig
) on your canvas. 因此,在您的中间代码中,您在pyplot
状态下调用Figure
对象上的imshow()
,但在画布上显示不同的图形( self.fig
)。
The problem is not that you need to use subplot
as such, but that you need to change the background image on the correct Figure
object. 问题不是您需要使用subplot
,而是需要更改正确的Figure
对象上的背景图像。 The way you've used subplot
in your potential fix code will do that - although I suggest an alternative below which maybe makes the intent clearer. 你在潜在的修复代码中使用subplot
的方式就是这样 - 虽然我建议在下面的替代方案可能会使意图更清晰。
Change 更改
plt.axis('off')
plt.tight_layout()
plt.imshow(image)
to 至
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 vs direct use of objects 关于根本原因的说明: pyplot
API与直接使用对象 This a bit of an opinion, but might help. 这有点意见,但可能有所帮助。 I tend to use the pyplot
interface when I need to quickly get things prototyped and want to use one of the fairly standard cases. 当我需要快速获取原型并希望使用其中一个相当标准的情况时,我倾向于使用pyplot
接口。 Often, that is enough. 通常,这就足够了。
As soon as I need to do more complicated things, I start to use the object model directly - maintaining my own named Figure
and Axes
objects etc. 一旦我需要做更复杂的事情,我就开始直接使用对象模型 - 维护我自己的名为Figure
和Axes
对象等。
Mixing the two is possible, but often confusing. 混合这两者是可能的,但往往令人困惑。 You've found this with your intermediate solution. 您已经在中间解决方案中找到了这个。 So I recommend doing one or the other. 所以我建议做一个或另一个。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.