I'm trying to mimic the legend
method in matplotlib.pyplot
where one can use loc='lower right'
to position the legend box fixed and properly aligned no matter the axis and the content of the box.
Using text
is out since this requires the manual input of the coordinates and I'm after something automatic.
I've tried using annotate and it gets me half the way there, but it still won't work right.
This is what I have so far:
import matplotlib.pyplot as plt
# Define some names and variables to go in the text box.
xn, yn, cod = 'r', 'p', 'abc'
prec = 2
ccl = [546.35642, 6785.35416]
ect = [12.5235, 13.643241]
fig = plt.figure()
ax = fig.add_subplot(111)
plt.xlim(-1., 10.)
plt.ylim(-1., 1.)
# Generate text to write.
text1 = "${}_{{t}} = {:.{p}f} \pm {:.{p}f}\; {c}$".format(xn, ccl[0],
ect[0], c=cod, p=prec)
text2 = "${}_{{t}} = {:.{p}f} \pm {:.{p}f}\; {c}$".format(yn, ccl[1],
ect[1], c=cod, p=prec)
text = text1 + '\n' + text2
ax.annotate(text, xy=(0.75, 0.9), xycoords='axes fraction', fontsize=10,
bbox=dict(facecolor='white', alpha=0.8),
horizontalalignment='left', verticalalignment='bottom')
plt.savefig('annotate_test.png', dpi=150)
which results in:
This will correctly scale for changing axis limits, but the problem is that: 1- it will fail if the axis are set to ax.set_aspect('equal')
:
and 2- it will fail if the text is too long (here I set prec=5
in the MWE above):
How can I tell matplotlib
to position the text box always in the top right corner and align it properly so it doesn't fall outside of the image (ie: what loc
does in legend
)?
The quick-and-dirty way is to use right and top aligned text and place it at a fixed offset in points from the axes corner:
import matplotlib.pyplot as plt
# Define some names and variables to go in the text box.
xn, yn, cod = 'r', 'p', 'abc'
prec = 2
ccl = [546.35642, 6785.35416]
ect = [12.5235, 13.643241]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.axis([-1, 10, -1, 1])
# Generate text to write.
text1 = "${}_{{t}} = {:.{p}f} \pm {:.{p}f}\; {c}$".format(xn, ccl[0],
ect[0], c=cod, p=prec)
text2 = "${}_{{t}} = {:.{p}f} \pm {:.{p}f}\; {c}$".format(yn, ccl[1],
ect[1], c=cod, p=prec)
text = text1 + '\n' + text2
ax.annotate(text, xy=(1, 1), xytext=(-15, -15), fontsize=10,
xycoords='axes fraction', textcoords='offset points',
bbox=dict(facecolor='white', alpha=0.8),
horizontalalignment='right', verticalalignment='top')
plt.show()
Because we've specified top and right alignment, it works with your two edge cases:
The downside of this is that the text is right-aligned. Ideally, you'd want the text alignment to be separate from the box alignment. The matplotlib.offsetbox
module has a number of methods to handle things like this.
If you want to mimic a legend box (down to the location codes), have a look at matplotlib.offsetbox.AnchoredText
. (Note that you can adjust the padding, etc though the pad
and borderpad
kwargs: http://matplotlib.org/api/offsetbox_api.html#matplotlib.offsetbox.AnchoredOffsetbox )
import matplotlib.pyplot as plt
import matplotlib.offsetbox as offsetbox
# Define some names and variables to go in the text box.
xn, yn, cod = 'r', 'p', 'abc'
prec = 5
ccl = [546.35642, 6785.35416]
ect = [12.5235, 13.643241]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.axis([-1, 10, -1, 1])
# Generate text to write.
text1 = "${}_{{t}} = {:.{p}f} \pm {:.{p}f}\; {c}$".format(xn, ccl[0],
ect[0], c=cod, p=prec)
text2 = "${}_{{t}} = {:.{p}f} \pm {:.{p}f}\; {c}$".format(yn, ccl[1],
ect[1], c=cod, p=prec)
text = text1 + '\n' + text2
ob = offsetbox.AnchoredText(text, loc=1)
ax.add_artist(ob)
plt.show()
One downside to this is that adjusting the font and box parameters for the result is a bit counter-intuitive. AnchoredText
accepts a dictionary of font parameters as the prop
kwarg. The box can be adjusted after initialization through the patch
attribute. As a quick example:
ob = offsetbox.AnchoredText(text, loc=1,
prop=dict(color='white', size=20))
ob.patch.set(boxstyle='round', color='blue', alpha=0.5)
ax.add_artist(ob)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With