具有可变小部件大小的回收视图不起作用(Kivy)
Recycle view with variable widget size not working (Kivy)
所以我有一个程序可以从服务器接收字符串并在将其添加到回收视图使用的列表之前对其进行格式化。当列表中的一个小部件的纹理大小发生变化时,会调用一个函数,因为它们的大小可能因内容而异。但是,每当添加更多项目时,回收视图就会完全出错并且所有项目都会重叠。
行为差异很大,所以我无法包含问题的每个示例,但我已经从我的程序中取出相关代码并制作了一个小示例程序,我将在下面包含它。我还添加了一些问题的图片。
我一直无法让它工作,也找不到任何有类似类似问题的人。任何帮助或链接将不胜感激,提前致谢
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)
意外行为图片
不同意外行为的图片
问题视频
https://youtu.be/F_2TLh-cFYA
备注
我会鼓励任何人 运行 此代码以完全理解问题,因为代码实际上在我第一次更改存储在列表 'posts'
中的内容时按预期运行
如何从内部设置标签 size
而不是使用 key_size
从外部设置标签。
您可以使用 minimum_height
保持固定高度的 viewclass
小部件,并通过标签的纹理高度控制其子小部件。
因此,您修改后的 PostGrid
和 kvlang
中的相关代码将如下所示,
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)
或者#您可以使用ScrollView
滚动扩展标签。但是这里每个 viewclass
小部件都将具有相同的高度。此外,由于内部小部件中存在 ScrollView
,您必须从一侧滚动小部件(这很常见)或根据 scroll_y
值自行操作触摸调度事件。
MASTER:
<MASTER>:
orientation: "vertical"
Button:
size_hint_y: 0.15
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
default_size: None, dp(64) # Specify it explicitly. Adjust to your need.
size_hint_y: None
padding: ["10dp", "16dp"]
spacing: "20dp"
height: self.minimum_height
orientation: 'vertical'
<PostGrid@BoxLayout>:
message_id: -1
orientation: "horizontal"
text: ''
_group: ''
_score: 0
spacing: "6dp"
text_size: None, None
size_hint_y: None
height: dp(100) # Adjust to your need.
BoxLayout:
id: voting_menu
orientation: "vertical"
spacing: "2dp"
size_hint_x: 0.2
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)
ScrollView:
effect_cls: "ScrollEffect"
canvas.before:
Color:
rgba: (0.8, 0.8, 0.8, 1)
RoundedRectangle:
size: self.size
radius: [5, 5, 5, 5]
pos: self.pos
Color:
rgba:0.8,0,0,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_y: None
height: self.texture_size[1]
font_size: "12dp"
text_size: self.width, None
color: 0,0,0,1
multiline: True
markup: True
#这是对这个的回应。
所以我有一个程序可以从服务器接收字符串并在将其添加到回收视图使用的列表之前对其进行格式化。当列表中的一个小部件的纹理大小发生变化时,会调用一个函数,因为它们的大小可能因内容而异。但是,每当添加更多项目时,回收视图就会完全出错并且所有项目都会重叠。
行为差异很大,所以我无法包含问题的每个示例,但我已经从我的程序中取出相关代码并制作了一个小示例程序,我将在下面包含它。我还添加了一些问题的图片。
我一直无法让它工作,也找不到任何有类似类似问题的人。任何帮助或链接将不胜感激,提前致谢
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)
意外行为图片
备注 我会鼓励任何人 运行 此代码以完全理解问题,因为代码实际上在我第一次更改存储在列表 'posts'
中的内容时按预期运行如何从内部设置标签 size
而不是使用 key_size
从外部设置标签。
您可以使用 minimum_height
保持固定高度的 viewclass
小部件,并通过标签的纹理高度控制其子小部件。
因此,您修改后的 PostGrid
和 kvlang
中的相关代码将如下所示,
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)
或者#您可以使用ScrollView
滚动扩展标签。但是这里每个 viewclass
小部件都将具有相同的高度。此外,由于内部小部件中存在 ScrollView
,您必须从一侧滚动小部件(这很常见)或根据 scroll_y
值自行操作触摸调度事件。
MASTER:
<MASTER>:
orientation: "vertical"
Button:
size_hint_y: 0.15
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
default_size: None, dp(64) # Specify it explicitly. Adjust to your need.
size_hint_y: None
padding: ["10dp", "16dp"]
spacing: "20dp"
height: self.minimum_height
orientation: 'vertical'
<PostGrid@BoxLayout>:
message_id: -1
orientation: "horizontal"
text: ''
_group: ''
_score: 0
spacing: "6dp"
text_size: None, None
size_hint_y: None
height: dp(100) # Adjust to your need.
BoxLayout:
id: voting_menu
orientation: "vertical"
spacing: "2dp"
size_hint_x: 0.2
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)
ScrollView:
effect_cls: "ScrollEffect"
canvas.before:
Color:
rgba: (0.8, 0.8, 0.8, 1)
RoundedRectangle:
size: self.size
radius: [5, 5, 5, 5]
pos: self.pos
Color:
rgba:0.8,0,0,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_y: None
height: self.texture_size[1]
font_size: "12dp"
text_size: self.width, None
color: 0,0,0,1
multiline: True
markup: True
#这是对这个