[ How can I fit text in label in Kivy? ]
I have a label with text that needs its font_size
to fit in some rectangle. I think I should get size in pixels like this (formulas):
# Device independent pixels
px = dp * density
# Scale independent pixels
px = sp * density * fontscale
# Points
px = pt * dpi / 72.
But problem is that I can't get dpi, density, fontscale, etc., it says:
TypeError: 'float' object is not callable
Also it seems there is no built-in function that can fit the text.
Answer 1
I think, I've found the solution.
class BlockLabel(Label):
scale_factor = .9
factor = dimension = None
def on_texture_size(self, *args):
if not self.factor:
self.factor = [self.font_size / self.texture_size[0], self.font_size / self.texture_size[1]]
if not self.dimension:
self.dimension = 1 if self.texture_size[0] * self.size[1] < self.texture_size[1] * self.size[0] else 0
self.font_size = self.size[self.dimension] * self.scale_factor * self.factor[self.dimension]
I've tested it only on squares (I mean text inscribed in square) for now, but it should work on rectangles too. Also if there are only squares, self.size
can be removed like this:
self.dimension = 1 if self.texture_size[0] < self.texture_size[1] else 0
And I've tested this code only for one-line text, I don't know if it works for multi-line text correctly, although it should.
Answer 2
I built up on @Necronomicron answer so that this codes works on rectangles. The problem (at least for me) was that the font_size calculation wasn't taking into account the width of the label, so if it got too small the letters would not be inside it.
I changed it to:
def on_texture_size(self, *args):
try:
if not self.factor:
self.factor = [self.font_size / self.texture_size[0], self.font_size / self.texture_size[1]]
self.font_size0 = self.size[0] * self.scale_factor * self.factor[0]
self.font_size1 = self.size[1] * self.scale_factor * self.factor[1]
if self.font_size0 < self.font_size1:
self.font_size = self.font_size0
else:
self.font_size = self.font_size1
except ZeroDivisionError:
pass
In this way the chosen font size is always the smallest, making sure that the text fits both vertically and horizontally. I also did this after the label (or button in my case) creation:
button1.bind(size=lambda x, y: button1.on_texture_size())
So that the text font_size is updated every time the button size changes (when the screen size changes, for example)