Строки в Kivy RecycleView повторяются
Я использую Kivy RecycleView с классом Row для создания редактируемой таблицы значений. Проблема в том, что когда я набираю запись в одной строке, это значение также появляется в других строках, расположенных ниже в таблице. В отладчике при редактировании строки корректно отображается RecycleView.data, обновленный только для той строки, которую я набрал, а не для других строк, где они появились, поэтому кажется, что существует разрыв между пользовательским интерфейсом и базовыми данными. Не могу понять, что я могу упустить или недопонимание с этим подходом. Пожалуйста помоги. Спасибо!
Я включил ссылку на видео о поведении кода ниже, так что, надеюсь, это говорит само за себя. Этого не происходит, когда начальные строки заполняются уникальными значениями.
На видео приложение имеет три кнопки. Левая кнопка заполняет случайные уникальные значения для строк. Поскольку эти строки заполнены уникальными значениями, их редактирование не вызывает ранее упомянутую проблему и ведет себя ожидаемым образом. Средняя и правая кнопки заполняют строки повторяющимся текстом "None" и пробелами соответственно. При заполнении строк данным этого типа редактирование любой из этих строк приведет к многократному появлению значения при прокрутке. Я использую Kivy 1.10.0 и Python 3.6.2 на OSX.
# based on code from https://github.com/kivy/kivy/blob/master/examples/widgets/recycleview/basic_data.py
from random import sample
from string import ascii_lowercase
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
kv = """
<Row@BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
TextInput:
multiline: False
text: root.value
on_text_validate: root.update(args[0].text)
<Test>:
canvas:
Color:
rgba: 0.3, 0.3, 0.3, 1
Rectangle:
size: self.size
pos: self.pos
rv: rv
orientation: 'vertical'
GridLayout:
cols: 3
rows: 1
size_hint_y: None
height: dp(108)
padding: dp(8)
spacing: dp(16)
Button:
text: 'Populate with text'
on_press: root.populate_with_text()
Button:
text: 'Populate with same text'
on_press: root.populate_with_same_text()
Button:
text: 'Populate with blanks'
on_press: root.populate_with_blanks()
RecycleView:
id: rv
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
viewclass: 'Row'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(2)
"""
class Row(RecycleDataViewBehavior, BoxLayout):
def update(self, text):
self.parent.parent.data[self.index]['value'] = text
self.parent.parent.refresh_from_data()
def refresh_view_attrs(self, rv, index, data):
self.index = index
return super(Row, self).refresh_view_attrs(rv, index, data)
class Test(BoxLayout):
def populate_with_text(self):
self.rv.data = [{'value': ''.join(sample(ascii_lowercase, 6)), 'index': x}
for x in range(50)]
self.rv.refresh_from_data()
def populate_with_same_text(self):
self.rv.data = [{'value': 'None', 'index': x}
for x in range(50)]
self.rv.refresh_from_data()
def populate_with_blanks(self):
self.rv.data = [{'value': '', 'index': x}
for x in range(50)]
self.rv.refresh_from_data()
Builder.load_string(kv)
class TestApp(App):
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
0 ответов
У меня была очень похожая проблема, возможно, вы уже решили ее по-своему, но, возможно, я помогу кому-то еще.
Может быть, вы заметили, что есть некоторые проблемы, связанные с этой проблемой на сайте Givub Kivy, но до сих пор без решения.
До сих пор я пришел к выводу, что это ошибка с recycleview в kivy.
Я не нашел реального решения, кроме личного обходного пути, фактически даже не 100% обходного пути, но для меня это сработало.
Мне немного стыдно публиковать этот тип обходного пути, но много часов работы над такой простой проблемой, как эта, заставляет вас принять некоторый "альтернативный" код для продвижения вперед.
Итак, чтобы проиллюстрировать это, я вставил в ваш код событие фокуса в файле kv внутри класса Row@BoxLayout, чтобы обновить визуальную ошибку, поэтому окончательный класс файла kv останется таким:
<Row@BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
TextInput:
multiline: False
text: root.value
on_text_validate: root.update(args[0].text)
on_focus: self.text = root.value ## new line bug workaround
Эта дополнительная строка кода "исправляет" ошибку после того, как она произошла, потому что ошибка не затрагивает реальный список, а только изменение экрана, поэтому событие фокуса обновляет значение с правильным текстом.
Проблема с этим подходом состоит в том, что если вы начинаете изменять текст в InputText и одновременно прокручиваете список, проблема произойдет, потому что событие validate будет выполнено в неправильной строке, но если вы просто измените текст и нажмете Enter, нет проблем.
Надеюсь, это кому-нибудь поможет.