I have a customized Text
object, which has the same logic as the following simplified object:
import matplotlib.pyplot as plt
from matplotlib.text import Text
class MyText(Text):
def __init__(self, x, y, txt, height, **kwargs):
super().__init__(x, y, txt, **kwargs)
self.height = height
def mydraw(self, ax):
txt = ax.add_artist(self)
myset_fontsize(txt, self.height)
return self
def set_height(self, height):
self.height = height
#myset_fontsize(self, height)
def myset_fontsize(txtobj, height):
trans = txtobj.get_transform()
pixels, _ = trans.transform((txtobj.height, 0)) - trans.transform((0,0))
dpi = txtobj.axes.get_figure().get_dpi()
points = pixels / dpi * 72
txtobj.set_fontsize(points)
if __name__ == '__main__':
fig, ax = plt.subplots()
ax.grid(True)
txt = MyText(0.2, 0.2, 'hello', 0.1)
txt.mydraw(ax)
MyText
is different from the built-in Text
in that the fontsize is dependent on the height
, which, for example, specifies the height of the text in the data coordinates. Except this, MyText
is almost the same as Text
. The example code gives the following figure:
This works fine for a static image. However, I want MyTest
to be interactive, which includes the following goals:
In an interactive plot mode,
txt.set_height(0.5)
shoule change the fontsize dynamically. I know I can add a snippet as the comment shows, but ifMyText
object is not added to the axes,txt.set_height(0.5)
will throw anAttributeError
. In short,txt.set_height()
should behave similarly totxt.set_fontsize()
.When the figure is resized by dragging the plot window,
MyText
should change the fontsize accordingly, that is, the height of text in the data coordinates should keep the same. But currently the fontsize is unchanged when resizing the figure. I have found this- When I change the aspect ratio of the figure,
MyText
should change the fontsize accordingly, same as the second point.
Thanks for any ideas!
CodePudding user response:
After reading the user guide, I found that every artist has a
stale
attribite, which is some signal to re-render the figure. The complete solution is as follows:import matplotlib.pyplot as plt from matplotlib.text import Text class MyText(Text): def __init__(self, x, y, txt, height, **kwargs): super().__init__(x, y, txt, **kwargs) self.height = height def __call__(self, event): # When calling myset_fontsize, `self.stale` will be `True` due to `self.set_fontsize()` in the function body. myset_fontsize(self, self.height) def mydraw(self, ax): txt = ax.add_artist(self) # Connect "draw_event" so that once a draw event happens, a new fontsize is calculated and mark the `Text` object is stale. ax.get_figure().canvas.mpl_connect('draw_event', self) return txt def set_height(self, height): self.height = height # When a new height is set, then the #`Text` object is stale, which will # forward the signal of re-rendering # the figure to its parent. self.stale = True def myset_fontsize(txtobj, height): trans = txtobj.get_transform() pixels, _ = trans.transform((txtobj.height, 0)) - trans.transform((0,0)) dpi = txtobj.axes.get_figure().get_dpi() points = pixels / dpi * 72 txtobj.set_fontsize(points)
This solution almost solves my problem although it's not perfect. It's a little inefficient.
Any improvements are appreciated.
CodePudding user response:
If you only need to change the font from the window size. Installed an event handler triggered by resizing the window. Fonts size-bound to one side of the window size(in this case to the width).
import matplotlib.pyplot as plt from matplotlib.text import Text class MyText(Text): def __init__(self, x, y, txt, height, **kwargs): super().__init__(x, y, txt, **kwargs) self.height = height self.event_text = fig.canvas.mpl_connect('resize_event', self.mysize) def mysize(event, ax): fig = plt.gcf() size_ = fig.get_size_inches() txt.set_fontsize(size_[0]*5) def mydraw(self, ax): txt = ax.add_artist(self) myset_fontsize(txt, self.height) return self def set_height(self, height): self.height = height # myset_fontsize(self, height) def myset_fontsize(txtobj, height): trans = txtobj.get_transform() pixels, _ = trans.transform((txtobj.height, 0)) - trans.transform((0, 0)) dpi = txtobj.axes.get_figure().get_dpi() points = pixels / dpi * 72 txtobj.set_fontsize(points) if __name__ == '__main__': fig, ax = plt.subplots() ax.grid(True) txt = MyText(0.2, 0.2, 'hello', 0.1) txt.mydraw(ax) plt.show()
- When I change the aspect ratio of the figure,