Skip to content
Advertisement

Kivy RecycleView of Labels without clipping text or huge spaces between

How can I make a RecycleView in a Kivy python app display all its labels without truncating the text contents of the label nor adding huge spaces in-between the labels?

I’m trying to display a very large amount of text in a Kivy (5+ MB) without causing it to lock-up. I think objectively the best solution here is to use a RecycleView with each line of the text in its own Label.

Official Documentation Demo

The example given in the official Kivy documentation about RecycleView is fine because the amount of text in the label is extremely short.

screenshot of the official documentation's demo showing a bunch of labels numbered "0" - "90" displaying in a scrollable window with white text and black background

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView


Builder.load_string('''
<RV>:
    viewclass: 'Label'
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
''')

class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'text': str(x)} for x in range(100)]


class TestApp(App):
    def build(self):
        return RV()

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

Demo with content

But if we update the example above so that the text in the label is actually substantial, mimicking real-world text, then the contents of the label’s text gets truncated. And there’s a huge space in-between each label.

screenshot of the "demo with content" showing similar output as the previous screenshot, except now the label text isn't a mere integer, but a very long string. It's obvious that both the end of the string and the beginning of the string are not visible as they run-on before and after the viewport of the app. There is also a very big space between each line of text.

import random

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView

Builder.load_string('''
<RV>:
    viewclass: 'Label'
    scroll_type: ['bars','content']
    bar_width: dp(25)
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
''')

class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'text': str(self.get_random())} for x in range(100)]

    def get_random(self):
        # generate some random ASCII content
        random_ascii = ''.join( [random.choice('0123456789abcdefghijklnmnoqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(0,900)] )
        random_ascii = 'START|' + random_ascii + '|END'
        print( random_ascii)
        return random_ascii

class TestApp(App):
    def build(self):
        return RV()

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

Demo with content and ‘text_size’

I’ve tried setting the text_size of the Label. That certainly displays much more of the text, but it’s still not showing all of the text in each Label.

In this example, the gap between each label is now gone.

screenshot of "demo with content and text_size" is similar to the previous screenshot, except the text of each line now wraps around on ~5 lines per label. Still, the beginning of the text of each line is truncated, but the end of the line is now visible. There is no longer a gap between each line.

import random

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView

Builder.load_string('''
<MyLabel@Label>:
    text_size: self.size
<RV>:
    viewclass: 'MyLabel'
    scroll_type: ['bars','content']
    bar_width: dp(25)
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
''')

class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'text': str(self.get_random())} for x in range(100)]

    def get_random(self):
        # generate some random ASCII content
        random_ascii = ''.join( [random.choice('0123456789abcdefghijklnmnoqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(0,900)] )
        random_ascii = 'START|' + random_ascii + '|END'
        print( random_ascii)
        return random_ascii

class TestApp(App):
    def build(self):
        return RV()

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

How can I display a vertical RecycleView of Labels such that the text contents of the Labels is not truncated, and there is no extra padding/margin between each row of Labels?

Advertisement

Answer

If you want the Label to stretch up as its text content, you can bind its width to its texture width. This will enable you to scroll horizontally within RecycleView. Again if you want to scroll vertically, you need to explicitly specify the height of each content (here Label).

Here’s a modified version (of the last one) of your kvlang,

<MyLabel@Label>:
    size_hint_x: None
    width: self.texture_size[0]
    # Canvas added for visual purpose.
    canvas.before:
        Color:
            rgb: 0.5, 0.5, 1
        Rectangle:
            size: self.size
            pos: self.pos


<RV>:
    viewclass: 'MyLabel'
    scroll_type: ['bars','content']
    bar_width: dp(25)
    RecycleBoxLayout:
        spacing: dp(1) # Adjust to your need (atyn).
        padding: dp(2) # atyn.
        default_size: None, dp(20) # atyn.
        default_size_hint: None, None
        size_hint: None, None
        size: self.minimum_size
        orientation: 'vertical'

Depending on the sample size (due to hardware) it may or may not be able to render the text. If so, try with smaller sample size (like in your examples 500/600 instead of 900).

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement