gquilarque
gquilarque

Reputation: 181

How I can adjust variable height text property kivy?

I have a ver long text in kivy. I want adjustment dynamic height depend of qty of text.

My code is this.

import kivy
from kivy.app import App 
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout


class DynamicHeight(App):y
    def build(self):
        grid = gl = GridLayout(cols=1)

        for i in range(3):
            l = Label(text='Text a longer line line line line line line line line', halign='left',text_size=(300, None))
            grid.add_widget(l)

        return grid

DynamicHeight().run()

I want that height of label or height row of gridlayout adjustment according to the amount of text.

Upvotes: 5

Views: 6796

Answers (4)

Tshirtman
Tshirtman

Reputation: 5949

Although there are solutions proposed already, i feel they don't leverage the kivy way of doing things, and that this way is cleaner. What you need is to bind the text_size to the available width, and bind the height of the widget to the rendered texture size.

from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout


class MyApp(App):
    def build(self):
        root = FloatLayout()

        b = GridLayout(
            cols=1,
            pos_hint={
                'center_x': .5,
                'center_y': .5},
            size_hint=(None, None),
            spacing=20,
            width=200)
        b.bind(minimum_height=b.setter('height'))
        root.add_widget(b)

        for text_length in range(0, 80, 20):
            l = Label(
                text='word ' * text_length,
                size_hint_y=None)
            l.bind(width=lambda s, w:
                   s.setter('text_size')(s, (w, None)))
            l.bind(texture_size=l.setter('size'))
            b.add_widget(l)

        return root


if __name__ == '__main__':
    MyApp().run()

Upvotes: 5

Casper Lehmann
Casper Lehmann

Reputation: 505

Following up on tshirtman's answer, I created code for doing the same thing in kv-lang. It might be a bit clearer what's going on, since you don't need to analyse the callback functions.

What's happening is that the label's width gets set in accordance with the layout, while the height gets set to the texture size of the text. So as the text string gets longer, the texture can only grow in height, which we can apply above, by setting the height of the boxlayout to my_label.height.

# -*- coding:utf8 -*-

from kivy.lang import Builder
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.core.window import Window
Window.size = (400, 700)

PLACEHOLDER_TEXT = u'''The bindings illustrated in tshirtman's example are created automatically when using kv-lang.
Notice how the "id: my_label" allows us to access the Label's attributes in the BoxLayout's height declaration.'''

kv_string = """
<Example>:
    orientation: 'vertical'
    BoxLayout:
        # colored background for affected area:
        canvas.before:
            Color:
                rgba: 0.3, .4, .4, .6
            Rectangle:
                pos: self.pos
                size: self.size
        size_hint_y: None
        height: my_label.height
        Label:
            id: my_label
            text: root.text
            font_size: '14dp'
            text_size: (self.width, None)
            size: self.texture_size
            valign: 'top'
            halign: 'left'
    BoxLayout:
        orientation: 'vertical'
        size_hint_y: 1
        canvas.before:
            Color:
                rgba: 0.9, .0, .5, .6
            Rectangle:
                pos: self.pos
                size: self.size
        TextInput:
            text: root.PLACEHOLDER_TEXT
            on_text: root.text = self.text
            text_size: self.size
            auto_indent: True
        Label:
            size_hint_y: None
            height: '50dp'
            text: 'String length: ' + str(len(root.text))
"""

Builder.load_string( kv_string )

class Example (BoxLayout ):
    PLACEHOLDER_TEXT = PLACEHOLDER_TEXT
    text = StringProperty(  )
    def __init__(self, **kwargs):
        super( Example, self).__init__(**kwargs)

class MyApp(App):
    def build(self):
        root = Example()
        return root

MyApp().run()

Upvotes: 0

gquilarque
gquilarque

Reputation: 181

I found the solution, with help of thopiekar.

For those needing this. So far I have not found kivy do without this method

import kivy
from kivy.app import App 
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button


class MultiLineLabel(Button):
    def __init__(self, **kwargs):
        super(MultiLineLabel, self).__init__( **kwargs)
        self.text_size = self.size
        self.bind(size= self.on_size)
        self.bind(text= self.on_text_changed)
        self.size_hint_y = None # Not needed here

    def on_size(self, widget, size):
        self.text_size = size[0], None
        self.texture_update()
        if self.size_hint_y == None and self.size_hint_x != None:
            self.height = max(self.texture_size[1], self.line_height)
        elif self.size_hint_x == None and self.size_hint_y != None:
            self.width  = self.texture_size[0]

    def on_text_changed(self, widget, text):
        self.on_size(self, self.size)


class DynamicHeight(App):
    def build(self):
        grid = GridLayout(cols=1,size_hint_x=None, width="300dp")

        l=['This Text very long, should add multiple lines, automatically. This Text very long, should add multiple lines, automatically', 'One line']

        for i in l:
            l = MultiLineLabel(text=i)
            grid.add_widget(l)
        return grid

DynamicHeight().run()

And works perfectly!!!!!

Upvotes: 2

Nykakin
Nykakin

Reputation: 8747

text.size() method isn't changing height atribute of Label. If text is too long, it will overlap with content below:

from kivy.app import App 
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout


class DynamicHeight(App):
    def build(self):
        layout = GridLayout(cols=1, spacing=20)

        l = Label(text='! '*100, text_size=(10, None),  size_hint_y=None, height=10)
        b = Button(text='...', size_hint_y=None)
        layout.add_widget(l)
        layout.add_widget(b)

        return layout

DynamicHeight().run()

You need to calculate the heigh of the text and set it with height attribute manually. I don't know any nice and clean way to do this. Here is a dirty way:

before = label._label.render()
label.text_size=(300, None)
after = label._label.render()
label.height = (after[1]/before[1])*before[1] # ammount of rows * single row height

Example:

from kivy.app import App 
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView

class DynamicHeight(App):
    def build(self):
        layout = GridLayout(cols=1, size_hint_y=None, spacing=20)
        layout.bind(minimum_height=layout.setter('height'))
        for i in xrange(1, 20):
            l = Label(text='Text ' * (i*10), text_size=(300, None), size_hint_y=None)

            # calculating height here 
            before = l._label.render()
            l.text_size=(300, None)
            after = l._label.render()
            l.height = (after[1]/before[1])*before[1] # ammount of rows * single row height
            # end

            layout.add_widget(l)
        root = ScrollView()
        root.add_widget(layout)
        return root

DynamicHeight().run()

Upvotes: 0

Related Questions