Open HDembinski opened 1 year ago
Well, for at least adding a link to the text, this works:
from matplotlib import pyplot as plt
fig = plt.figure()
plt.scatter([1, 2], [4, 6], label="BBC")
plt.scatter([1, 2, 3], [6, 5, 4], label="Google")
leg = plt.legend()
for ta in leg.texts:
t = ta.get_text()
if t == "BBC":
url = 'https://www.bbc.com/news'
ta.set_url(url)
fig.savefig('scatter.svg')
I may try a little more to add to the artist portion of the legend, possibly, but this is a start, at least.
Nice! But it is not ideal.
To click on the link you really need to be exactly on top of the text, which requires pixel-accurate pointing. It would be better if the whole TextArea was clickable instead.
Also, but this is a separate issue: I don't understand why your code works and my code does not.
To click on the link you really need to be exactly on top of the text, which requires pixel-accurate pointing. It would be better if the whole TextArea was clickable instead.
I'm not clear what you mean here. The pointer has to be over the word, but you want the hit area to be larger somehow?
@jklymak by default svg exports text as paths, and the link function requires you to click on the actual text itself (e.g. you cannot click in the middle of C
, you have to click on the line.
Using:
plt.rcParams['svg.fonttype'] = 'none'
Causes the svg backend to use SVG text objects instead, which alleviates that particular problem. Setting to "svgfont" instead of "none" will embed the font, if that matters to you, rather than relying on the viewing machine to have the font installed/use a fallback font.
OK, thanks, I can see that now. PDF doesn't seem to suffer from this behaviour.
As to why your original code didn't work, what you get to is the TextArea
object, not the Text
object that leg.texts
gets, the latter is actually a child, so you would need to do ta.get_children()[0].set_url(url)
I suspect a similar thing is true of the DrawArea
vs PathCollection
of the original, though the thing that actually accepts a url may in fact be deeper yet. (May also need to be set_urls
, plural, on the collection, but the obvious things didn't seem to work yet for the legend like they do the actual data. I suspect it may be being lost in redraw, as a new collection is made, possibly?)
@ksunden @jklymak Thank you for this feedback and the explanation, this all makes sense.
How do we go from here? After learning all this, I don't think that matplotlib needs a new API to make this easier, but it would be nice to add an example to the docs with this recipe, because there are some aspects which are not obvious, like setting plt.rcParams['svg.fonttype']
to 'none'
or 'svgfont'
.
Edit: I confirm that the amended recipe works as intended with SVG and PDF images and solves my problem. Thank you!
Edit2: I further confirm that this also works when the plots are embedded in a Jupyter notebook if
%config InlineBackend.figure_formats = ['svg']
is set.
Problem
tl;dr It would be great if URLs could be assigned to legend entries, so that a click on the legend entry in a SVG image brings you to the URL.
I am generating plots such as this one:
(Source: https://github.com/crdb-project/tutorial/blob/master/gallery.ipynb)
which includes a lot of data from other papers. The table in gray gives credit to those papers, which are listed via a key that is understood in the field (it is a key from this database https://ui.adsabs.harvard.edu, but that is an irrelevant detail).
It would be super nice if the entries in that table were clickable and would bring you to a page with that paper. I have the URLs, so I only need a way to make clickable legend entries, because this table is actually generated with a matplotlib legend object (this is also a detail, I could have programmed a proper table using VPacker, HPacker, and TextArea, but just misusing a legend was the simplest option).
Some searching made me aware of this feature here: https://matplotlib.org/stable/gallery/misc/hyperlinks_sgskip.html which works great to assign URLs to points. But I cannot get it to work for legend entries. Specifically, I tried this code: