Reputation: 181
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
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
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
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
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