So I have a program that receives a string from a server and formats it before adding it to a list used by a recycleview. There is a function that is called when the texture size of one of the widgets in the list changes as they can be different sizes depending on the content. However, whenever more items are added the recycleview goes completely wrong and all of the items overlap.
The behavior varies a lot so I cannot include every example of the issue but I have taken out the relevant code from my program and made a small example program which I will include below. I have also added some images of the issue.
I have been unable to get this to work or find anyone with similar a similar issue. Any help or links will be greatly appreciated, thanks in advance
main.py
from kivy.app import App
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
example_string = 'test)£++£(0)+££+(test)£++£(0)+££+(test)£++£(0)+££+(test)£++£(0)+££+(test)£++£(0'
class MASTER(BoxLayout):
def example_button(self, Button): # Demo of list updating
global example_string # I know
example_string += ')+££+(test)£++£(0'
temp = []
id_num = 0
for post in example_string.split(')+££+('): # The string is an example of the input data
temp.append({'message_id':id_num, 'text':('[font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\n' + post.split(')£++£(')[0]), '_size':[0,0], '_group':str(id_num), '_score':int(post.split(')£++£(')[1])})
id_num = id_num + 1
App.get_running_app().posts = temp
class DemoApp(App):
# One post format = {'message_id':0, 'text':'post_test_here','_size':[0,0], '_group':str(0), '_score':20}
# Text fromat string = [font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\n
posts = ListProperty([{'message_id':0, 'text':'[font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\nHello, this is a test of a post jhghjgjhgh','_size':[0,0], '_group':str(0), '_score':20}, {'message_id':1, 'text':'[font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\nHello, this is a test of a post hghghghghghghg', '_size':[0,0], '_group':str(1), '_score':100}, {'message_id':2, 'text':'post_test_her\n\ne','_size':[0,0], '_group':str(2), '_score':20}])
def update_message_size(self, message_id, texture_size):
self.posts[message_id] = {**self.posts[message_id], '_size':[1, texture_size[1]]}
print('Message ID = ' + str(message_id))
print('Texture size = ' + str(texture_size))
def up_vote(self, button, mode): # Not part of the problem
if button.state == 'down':
if mode == 'all':
print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
else:
print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')
def down_vote(self, button, mode): # Not part of the problem
if button.state == 'down':
if mode == 'all':
print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
else:
print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')
if __name__ == '__main__':
DemoApp().run()
demo.kv
MASTER:
<MASTER>:
Button:
text: 'Add items'
on_press: root.example_button(self)
RecycleView:
viewclass: 'PostGrid'
scroll_y: 1
id: rv
data: app.posts
canvas.before:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
RecycleBoxLayout:
id: box
default_size_hint: 1, None
size_hint_y: None
padding: ["10dp", "16dp"]
spacing: "20dp"
height: self.minimum_height
orientation: 'vertical'
key_size: '_size'
<PostGrid@BoxLayout>:
message_id: -1
orientation: "horizontal"
text: ''
_group: ''
_score: 0
spacing: "6dp"
text_size: None, None
BoxLayout:
id: voting_menu
orientation: "vertical"
spacing: "2dp"
size_hint: .2, 1
size: self.size
ToggleButton:
id: button_up
on_state: app.up_vote(self, 'all')
group: str(root._group)
text: "UP"
color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
font_size: "10dp"
size_hint: 1, .3
background_color: .2, .2, .2, 0
canvas.before:
Color:
rgba: (.1,.1,.1,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [6,]
canvas:
Color:
rgba: .2,.2,.2,1
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
Label:
id: vote_count
text: str(root._score)
size_hint: 1, .4
multiline: False
ToggleButton:
id: button_down
on_state: app.down_vote(self, 'all')
group: str(root._group)
text: "DOWN"
color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
font_size: "10dp"
size_hint: 1, .3
background_color: .2, .2, .2, 0
canvas.before:
Color:
rgba: (.1,.1,.1,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [6,]
canvas:
Color:
rgba: (.2,.2,.2,1)
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
Label:
text: root.text
padding: "10dp", "12dp"
size_hint: .9, 1
height: self.texture_size[1]
font_size: "12dp"
text_size: self.width, None
color: 0,0,0,1
multiline: True
markup: True
on_texture_size: app.update_message_size(root.message_id, self.texture_size)
pos: self.pos
canvas.before:
Color:
rgba: (0.8, 0.8, 0.8, 1)
RoundedRectangle:
size: self.texture_size
radius: [5, 5, 5, 5]
pos: self.x, self.y
canvas:
Color:
rgba:0.8,0,0,1
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
Image of unexpected behavior Image of different unexpected behavior Video of problem https://youtu.be/F_2TLh-cFYA
Note I would encourage anyone to run this code to fully understand the problem since the code actually behaives as expected the first time I change what is stored in the list 'posts'
How about setting size
of the labels from inside instead of setting it from outside using key_size
.
You might keep the viewclass
widget of fixed height using the minimum_height
and control its child widget by the label's texture height.
Thus your modified PostGrid
and associated code in kvlang
will look something like this,
RecycleBoxLayout:
id: box
default_size_hint: 1, None
default_size: None, dp(50) #
size_hint_y: None
padding: ["10dp", "16dp"]
spacing: "20dp"
height: self.minimum_height
orientation: 'vertical'
# key_size: '_size'
# Remove the associated prop. from python (i.e. from method 'example_button' etc.) as well.
<PostGrid@BoxLayout>:
message_id: -1
orientation: "horizontal"
text: ''
_group: ''
_score: 0
spacing: "6dp"
text_size: None, None
size_hint_y: None
height: self.minimum_height
BoxLayout:
id: voting_menu
orientation: "vertical"
spacing: "2dp"
size_hint: .2, None
height: label.height # This binding will force voting_menu to resize.
# size: self.size # I don't think it has any effect.
ToggleButton:
id: button_up
on_state: app.up_vote(self, 'all')
group: str(root._group)
text: "UP"
color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
font_size: "10dp"
size_hint: 1, .3
background_color: .2, .2, .2, 0
canvas.before:
Color:
rgba: (.1,.1,.1,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [6,]
canvas:
Color:
rgba: .2,.2,.2,1
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
Label:
id: vote_count
text: str(root._score)
size_hint: 1, .4
multiline: False
ToggleButton:
id: button_down
on_state: app.down_vote(self, 'all')
group: str(root._group)
text: "DOWN"
color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
font_size: "10dp"
size_hint: 1, .3
background_color: .2, .2, .2, 0
canvas.before:
Color:
rgba: (.1,.1,.1,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [6,]
canvas:
Color:
rgba: (.2,.2,.2,1)
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
Label:
id: label # For reference.
text: root.text
padding: "10dp", "12dp"
size_hint: .9, None
height: self.texture_size[1]
font_size: "12dp"
text_size: self.width, None
color: 0,0,0,1
multiline: True
markup: True
# on_texture_size: app.update_message_size(root.message_id, self.texture_size)
# pos: self.pos
canvas.before:
Color:
rgba: (0.8, 0.8, 0.8, 1)
RoundedRectangle:
size: self.texture_size
radius: [5, 5, 5, 5]
pos: self.x, self.y
canvas:
Color:
rgba:0.8,0,0,1
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)