Search code examples
pythonmatplotliblocationlegend

How to replace matplotlib legend and keep same location?


I'd like to replace a matplotlib legend with a new one, but keep the same (possibly arbitrary) location of the legend being replaced.

Why won't the following code work?

import matplotlib.pyplot as plt

plt.plot(range(10))
ax = plt.gca()

leg = plt.legend(['a'], loc='lower left')
bb = leg.get_bbox_to_anchor().inverse_transformed(ax.transAxes)

plt.legend(['b'], bbox_to_anchor=bb)

Solution

  • To answer the question why the code in the question wouldn't work:
    The legend's position is determined only at runtime when the figure is drawn. Additionally the bbox_to_anchor is not set, so it will be the complete axes ((0,0,1,1)).

    What you can do is first draw the canvas and then obtain the window extent

    ax.figure.canvas.draw()
    bb = leg.get_window_extent().inverse_transformed(ax.transAxes)
    

    This however will not give you the correct position (only close to it).

    Now a possible solution might be not to replace the legend, but instead only update it's content:

    import matplotlib.pyplot as plt
    
    plt.plot(range(10))
    ax = plt.gca()
    
    leg = plt.legend(['a'], loc='lower left')
    
    leg.texts[0].set_text("b")
    
    plt.show()
    

    Otherwise, if updating is not an option, you would need the bbox_to_anchor and the loc parameter from the old legend:

    import matplotlib.pyplot as plt
    
    plt.plot(range(10))
    ax = plt.gca()
    
    leg = plt.legend(['a'], loc='lower left', bbox_to_anchor=(0.5,0.5))
    
    bb = leg.get_bbox_to_anchor().inverse_transformed(ax.transAxes)
    plt.legend(['b'], bbox_to_anchor=bb, loc=leg._loc_real) 
    
    plt.show()
    

    This may still fail in cases, where mode has been set to "expand". It is also only accurate when the same fancybox paramters are used to create the new legend.