Here is a tkinter program, boiled down from a GUI I am working on:
import tkinter as tk
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
class App(tk.Tk):
def __init__(self):
super(App, self).__init__()
self.main_frame = tk.Frame(self,)
self.main_frame.pack(fill=tk.BOTH, expand=1)
self.plot1_button = tk.Button(self.main_frame, text='Plot 1',
command=self.draw_plot1)
self.plot1_button.pack(fill=tk.X,expand=1)
self.plot2_button = tk.Button(self.main_frame, text='Plot 2',
command=self.draw_plot2)
self.plot2_button.pack(fill=tk.X,expand=1)
self.FIG, self.AX = plt.subplots()
self.canvas = FigureCanvasTkAgg(self.FIG, master=self.main_frame)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2Tk(self.canvas, self.main_frame)
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def draw_plot1(self):
self.clear_axes()
fig = self.AX.plot(np.random.rand(10),np.random.rand(10), color='red')
self.canvas.draw_idle()
self.toolbar.update()
def draw_plot2(self):
self.clear_axes()
im = self.AX.matshow(np.random.rand(100,100))
self.canvas.draw_idle()
self.toolbar.update()
cb = plt.colorbar(im, ax=self.AX)
def clear_axes(self):
for ax in self.FIG.axes:
ax.clear()
if ax != self.AX:
ax.remove()
root = App()
root.resizable(False, False)
root.mainloop()
The Plot 1 button draws a random line plot, while the Plot 2 button draws a random heatmap with a colorbar. The Plot 1 button can be clicked repeatedly, creating new random line plots as expected. After 10 clicks, the display looks fine:
But the Plot 2 button causes the figure to shrink each time it is clicked. After 10 clicks, the graph is uninterpretable:
Additionally, the figure size persists when clicking Plot 1 again:
These are the .png
files saved from the application's toolbar, but the same can be seen in the GUI window. I have tried add updates to the GUI/canvas (e.g. self.update()
, self.canvas.draw_idle()
) at different locations but haven't found anything that affects the issue. I added the clear_axes()
function because in the real GUI I have some figures with multiple axes
and this removes them, but apparently it does not help here.
I have found that if the color bar is removed, the problem disappears (i.e. comment out cb = plt.colorbar(im, ax=self.AX)
), but I would like to have this as part of the figure. Can anyone shed light on what is going on, or can anyone suggest a fix? I'm on matplotlib 3.2.1
.
The problem is you are not clearing the colorbar
when you clear the axes.
class App(tk.Tk):
def __init__(self):
super(App, self).__init__()
self.main_frame = tk.Frame(self,)
...
self.cb = None
...
def draw_plot2(self):
self.clear_axes()
im = self.AX.matshow(np.random.rand(100,100))
self.canvas.draw_idle()
self.toolbar.update()
self.cb = plt.colorbar(im, ax=self.AX)
def clear_axes(self):
if self.cb:
self.cb.remove()
self.cb = None
for ax in self.FIG.axes:
ax.clear()
if ax != self.AX:
ax.remove()
Also note that you should use matplotlib.figure.Figure
instead of pyplot
when working with tkinter
. See this for the official sample.