Search code examples
pythonkivykivy-language

How to dynamically adjust the size of text according to the screen in .kv file


So I need to print a list on my screen. This list can change in size and has a limit of 15 elements, meaning 15 lines (\n). With every new line comes a new name. This list will be printed above a large button that must cover the whole list. I can manage to align the text neatly in the button but as soon as the screen size changes the text off sets since the button dynamically changes according to the screen size.

Below you can see my code for that specific class and although I dont think this will be of any use I thought I would include it anyway

class RecordData(Screen):

    def printNames(self):
        with open("Lipo names.txt") as f:
            lineList = f.readlines()
        lipoNames = ("".join(map(str, lineList)))
        self.names.text = lipoNames
        self.remove_widget(self.remove)

As you can see I have been playing around with the font_size and this now dynamically changes but even if the screen only stretches in the y axis it will also adjust both the x and y size of the text. I want the y size of the text to change whenever the y of the screen changes and not both x and y.

<RecordData>
    name: "record"
    names: names
    remove: remove

    Button:
        background_color: 255,255,255,1
        pos_hint: {"x": 0.05, "y": 0.05}
        size_hint: 0.2, 0.9


    Button:
        id: remove
        pos_hint: {"x": 0.09, "y": 0.5}
        size_hint: 0.2, 0.1
        text: "View lipos"
        font_size: (root.width**2 + root.height**2) / 14**4
        on_release:
            root.printNames()

    Label:
        id: names
        text: ""
        color: 0,0,0,1
        font_size: (root.width**2 + root.height**1.9) / 13**4
        pos_hint:{"x": 0.0, "y": 0.32}
        size_hint:0.3, 0.15

Here is a image of what currently prints with the default tab open

Here is a picture when I put the tab into full screen, as you can see the words have become massive because of the multiplier that I have added. But If I don't add this the words remain small

I hope this makes sense. This is my first kivy app so I am still very new to this kv language so can you please keep your explanations simple. Thank you in advance for all the help!


Solution

  • You can calculate the font size in at least two different ways. The simplest is just to use a formula as you have done. I found the following seems to work:

    Button:
        id: butt  # added id
        background_color: 255,255,255,1
        pos_hint: {"x": 0.05, "y": 0.05}
        size_hint: 0.2, 0.9
    Label:
        id: names
        text: ""
        color: 0,0,0,1
        pos_hint: butt.pos_hint  # same position as the Button
        size_hint: 0.3, butt.size_hint_y  # same height as the Button
        
        # set text area to same size as the Label
        text_size: self.size
        
        # position the text in the text area
        valign: 'center'
        halign: 'left'
    
        font_size: (self.height / 15) * 0.75
    

    Another way is to calculate the font size using kivy.core.text.Label by setting triggers to calculate the font size when the text or Label size change:

    <RecordData>
        name: "record"
        names: names
        remove: remove
    
        Button:
            id: butt  # added id
            background_color: 255,255,255,1
            pos_hint: {"x": 0.05, "y": 0.05}
            size_hint: 0.2, 0.9
    
    
        Button:
            id: remove
            pos_hint: {"x": 0.09, "y": 0.5}
            size_hint: 0.2, 0.1
            text: "View lipos"
            font_size: (root.width**2 + root.height**2) / 14**4
            on_release:
                root.printNames()
    
        Label:
            id: names
            text: ""
            color: 0,0,0,1
            pos_hint: butt.pos_hint  # same position as the Button
            size_hint: 0.3, butt.size_hint_y  # same height as the Button
            
            # set text area to same size as the Label
            text_size: self.size
            
            # position the text in the text area
            valign: 'center'
            halign: 'left'
            
            # recalculate font size when text or size changes
            on_size: root.adjust_font_size()
            on_text: root.adjust_font_size()
    

    Then, the adjust_font_size() method is added to the RecordData class:

    def adjust_font_size(self):
        names = self.ids.names
        if names.text == '':
            return
        font_size = 15
        tmp_label = CoreLabel(text=names.text, font_size=font_size, font_name=names.font_name)
        tmp_label.refresh()
    
        # maximize the font size
        while( tmp_label.height <= names.height):
            font_size += 1
            tmp_label = CoreLabel(text=names.text, font_size=font_size, font_name=names.font_name)
            tmp_label.refresh()
    
        # at this point font size will be a bit too big
        # reduce font size just until text fits in the names Label
        while( tmp_label.height > names.height):
            font_size -= 1
            tmp_label = CoreLabel(text=names.text, font_size=font_size, font_name=names.font_name)
            tmp_label.refresh()
    
        # set the calculated font size in the names Label
        names.font_size = font_size
    

    This code only considers the height of the Label, but could be extended to also consider the width.