This is an old question, but I actually had a need for underlining text that didn't use LaTeX so I figured I'd follow up with the solution that I came up with, for others who may encounter the same issue.
Ultimately my solution finds a bounding box for the text object in question and then uses the arrowprop arguments in the annotation command in order to draw a straight line underneath the text. There are some caveats with this approach, but overall I find it quite flexible because then you can customize the underline however you'd like.
An example of my solution is as follows:
import matplotlib.pyplot as plt
import numpy as np
def test_plot():
f = plt.figure()
ax = plt.gca()
ax.plot(np.sin(np.linspace(0,2*np.pi,100)))
text1 = ax.annotate("sin(x)", xy=(.7,.7), xycoords="axes fraction")
underline_annotation(text1)
text2 = ax.annotate("sin(x)", xy=(.7,.6), xycoords="axes fraction",
fontsize=15, ha="center")
underline_annotation(text2)
plt.show()
def underline_annotation(text):
f = plt.gcf()
ax = plt.gca()
tb = text.get_tightbbox(f.canvas.get_renderer()).transformed(f.transFigure.inverted())
# text isn't drawn immediately and must be
# given a renderer if one isn't cached.
# tightbbox return units are in
# 'figure pixels', transformed
# to 'figure fraction'.
ax.annotate('', xy=(tb.xmin,tb.y0), xytext=(tb.xmax,tb.y0),
xycoords="figure fraction",
arrowprops=dict(arrowstyle="-", color='k'))
#uses an arrowprops to draw a straightline anywhere on the axis.
and this produces an underlined annotated sin example.
One thing to note is that if you want to pad the underline, or control the line thickness (note that the line thickness is the same for both annotations) you'll have to do it manually within the 'underline_annotation' command, but this is pretty easy to do by passing more arguments through the arrowprops dict, or augmenting the location of where the line is being drawn.