How to implement recycleview with dynamic height size rows

348 views
Skip to first unread message

Ruakuu Kyuzo

unread,
Aug 12, 2020, 5:27:02 PM8/12/20
to Kivy users support
I been trying to implement recycle view to look at big log files, the idea is to have one label per line with no spacing in between rows. Lines can vary in text length so the label needs to adjust itself to fit it. So far I got the base code in, labels wrap to text with small separation. The problem happens when I start scrolling, lower rows shows labels bigger that it should, then it sometimes skips and labels try to adjust, then getting at the end some cluttered text in the bottom. I need scroll back up then down again to fix. Scrolling back from bottom to top doesn't show problems. Here is the code I have, if anyone has suggestions on how this can be better implemented will be appreciated.

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

Builder.load_string('''
<Row@Label>:
canvas.before:
Color:
rgba: 0.8, 0.1, 0.1, 0.5 #Red Marker
Rectangle:
size: self.size
pos: self.pos
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
font_size: dp(20)

<RV>:
viewclass: 'Row'
RecycleBoxLayout:
default_size: None, dp(20)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(3)

''')


class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
line = ''
for i in range(50):
n = random.randint(0, 1)
if n:
j = random.randint(5, 30)
line = 'Line: ' + str(i+1) + ' This is a test of a bunch of text' * j
else:
line = 'Line: ' + str(i+1) + ' This is a test of a bunch of text'
self.data.append({'text': line})


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


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

Elliot Garbus

unread,
Aug 13, 2020, 11:34:05 AM8/13/20
to kivy-...@googlegroups.com

The code below forces a texture update, and things are sized properly.  It does create a side effect on initial scroll that the list seems to jump. 

 

In a recycle view, the number of widgets is limited to the number you see on the screen, they are recycled.  As they are getting reused they are getting resized.  I don’t see a clean way around this.

 

Here are a few things to try:

  1. Use ScrollView, rather than a Recycle view.  There is no recycling, see if the size of the log causes a performance issue.
  2. Use the recycle view and program the code to scroll to the bottom, and then the top, see if that adequately refreshes all of the widgets.  Set scroll_y to 0 and then 1.  May need to use the Clock to wait for a frame so things resize.  More specifically try scheduling the 2 scroll actions.
  3. Use a RecycleView, but make row a constant size.  Use color or a blank row to distinguish between lines from the original source.

 

 

 
<Row@Label>:
    canvas.before:
        Color:
            rgba: 0.8, 0.1, 0.1, 0.5 #Red Marker
        Rectangle:
            size: self.size
            pos: self.pos
    text_size: self.width, None
    size_hint_y: None
    height: self.texture_size[1]
    font_size: dp(20)
    on_size: 
        self.texture_update()
        self.height = self.texture_size[1]

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/282b64f2-17be-43f3-b049-380dfbd1b821o%40googlegroups.com.

 

Elliot Garbus

unread,
Aug 13, 2020, 4:40:59 PM8/13/20
to Kivy users support
Fascinated with this issue I tried a few things.  Scrolling to top and bottom, option 2 below, did not change the behavior.
Here is the code using a scrollview.  I extended the number of items to 200.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label

import random

kv
= '''
<Row>:
    canvas.before:
        Color:
            rgba: 0.8, 0.1, 0.1, 0.5 #Red Marker
        Rectangle:
            size: self.size
            pos: self.pos
    text_size: self.width, None
    size_hint_y: None
    height: self.texture_size[1]
    font_size: dp(20)

BoxLayout:
    orientation: 'vertical'
    ScrollView:
        do_scroll_x: False
        do_scroll_y: True
        scroll_type:['bars', 'content']
        bar_width: 20
        BoxLayout:
            orientation: 'vertical'
            spacing: dp(3)
            id: sv_y
            size_hint_y: None
            height: self.minimum_height
'''


class Row(Label):
   
pass


class TestApp(App):
   
def build(self):
       
return Builder.load_string(kv)

   
def on_start(self):
       
for i in range(200):

            n
= random.randint(0, 1)
           
if n:
                j
= random.randint(5, 30)

                line
= 'Line: ' + str(i + 1) + ' This is a test of a bunch of text' * j
               
self.root.ids.sv_y.add_widget(Row(text=line))
           
else:
                line
= 'Line: ' + str(i + 1) + ' This is a test of a bunch of text'
                self.root.ids.sv_y.add_widget(Row(text=line))

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+unsubscribe@googlegroups.com.

Message has been deleted

Ruakuu Kyuzo

unread,
Aug 14, 2020, 4:15:32 AM8/14/20
to Kivy users support
Adding those 3 lines to trigger an update on the texture did make the scrolling more smooth for me. In the implementation I did the font is smaller and each line is no more than a 3 line paragraph so the skip glitch is barely notizable. This will be the final example code for this as I added a few things to read each line more easily:

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

Builder.load_string('''
<Row@Label>:
canvas.before:
Color:
rgba: 0.8, 0.1, 0.1, 0.5 #Red Marker
Rectangle:
size: self.size
pos: self.pos
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
    font_size: dp(18)
markup: True
    on_size: 
self.texture_update()
self.height = self.texture_size[1]

<RV>:
viewclass: 'Row'
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
    RecycleBoxLayout:
default_size: None, dp(20)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(3)

''')


class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
line = ''
        for i in range(200):

n = random.randint(0, 1)
if n:
j = random.randint(5, 30)
                line = '[color=#ffff00]Line: ' + str(i + 1) + '[/color] This is a test of a bunch of text' * j
else:
line = '[color=#ffff00]Line: ' + str(i + 1) + '[/color] This is a test of a bunch of text'
            self.data.append({'text': line})


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


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

I also run your example using scrollview, it's scrolls more smooth than the recycle view implementation, it will be good for small files, but I'm worried that having several files open with more than 10,000 lines will surely degradate performance.

Anyway thanks for jumping on this, really appreciate your help

Elliot Garbus

unread,
Aug 14, 2020, 10:51:12 AM8/14/20
to kivy-...@googlegroups.com

😎

 

From: Ruakuu Kyuzo
Sent: Friday, August 14, 2020 1:15 AM
To: Kivy users support

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.

To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/4c29e94b-64cd-4319-aa8d-59f795f4bf4eo%40googlegroups.com.

 

Reply all
Reply to author
Forward
0 new messages