同事程序员提醒我一个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
变种的任何好文档。
答案 0 :(得分:2)
因此,问题是,为什么我们现在需要使用子图(使用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
接口。通常,这就足够了。
一旦我需要做更复杂的事情,我就开始直接使用对象模型 - 维护我自己的名为Figure
和Axes
对象等。
混合两者是可能的,但经常令人困惑。您已经通过中间解决方案找到了这个。所以我建议做一个或另一个。