TAGS :Viewed: 7 - Published at: a few seconds ago

[ 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)