This question is most likely related to a previous question I had that arose during refactoring some code (to re-use the matplotlib axes object) that is linked here. That question described an issue where using imshow
would adjust the image.aspect
parameter.
I am now encoutering an issue that gives a similar effect but I have been unable to fix it by forcing the image.aspect
parameter to auto, which was my first thought as it looks like that is being adjusted somehow. I have also been unable to find anything digging through the underlying documentation found here.
Here are pictures illustrating the issue (Obscured legend entries):
Expected behaviour (limited number of legend entries)
Unexpected behaviour (large number of legend entries)
A MVCE for the above behaviour (with utter nonsense data) is listed below, where the behaviour can be toggled between expected and unexpected (for me) by toggling the upper limit in the for i in range(1,10):
line to 50.
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg
)
import tkinter as tk
import numpy
from matplotlib import image, figure
class Foo(object):
@classmethod
def run(cls):
root = tk.Tk()
Foo(root)
root.mainloop()
def __init__(self, master):
self.fig = figure.Figure(figsize=(12,6))
self.fig.set_tight_layout(True)
self.axes = self.fig.add_subplot(111)
self.axes.axis('off')
self.canvas = FigureCanvasTkAgg(self.fig, master=master)
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=tk.YES)
self.canvas.draw()
self.data = []
# Adjust the upper value to 10 or 50
for i in range(1,10):
self.axes.plot(range(0,100),numpy.random.randint(1,100,100), label=str(i))
self.axes.legend()
if __name__ == '__main__':
Foo.run()
I would love to get a hint as to what parameter or setting I am struggling with this time.
Here tight_layout
is trying to fit every artist drawn, into the figure with as little padding as possible. When the number of legend entries is very large this will give some undesired results.
In matplotlib versions > 3 an artist has a property set_in_layout
(docs for tight_layout) which you can set to either True or False to include or omit it from the calculations done when tight_layout
is called. Therefore you can modify your __init__
to look something like:
def __init__(self, master):
self.fig = figure.Figure(figsize=(12,6))
self.fig.set_tight_layout(True)
self.axes = self.fig.add_subplot(111)
self.axes.axis('off')
self.canvas = FigureCanvasTkAgg(self.fig, master=master)
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=tk.YES)
self.canvas.draw()
self.data = []
# Adjust the upper value to 10 or 50
for i in range(1,50):
self.axes.plot(range(0,100),numpy.random.randint(1,100,100), label=str(i))
self.leg = self.axes.legend()
self.leg.set_in_layout(False) # set to False so it's not used in tight_layout calculation!
An alternative (but less than ideal) option would be to remove self.fig.set_tight_layout(True)
and adjust the whitespace yourself using self.fig.subplots_adjust()
, however this may require some manual tweaking of the parameters to get the desired result.