Как выбрать всю строку в RecycleView с помощью Kivy

Я новичок в python, но совсем новичок в Kivy. Я считаю, что документацию вокруг Kivy трудно интерпретировать, по крайней мере, для меня. До сих пор мне удалось создать RecycleView, где я могу добавлять, удалять и изменять данные. В идеале я хочу иметь столько столбцов, сколько захочу, но все данные в каждой строке принадлежат друг другу. Следовательно, если я выберу один, я хочу выделить все элементы в этой строке. Я пробовал с togglebutton, но безуспешно. Я просто не знаю, как я могу получить доступ к каждому отдельному переключателю в RecycleView через идентификаторы или какой-либо другой метод. Если бы я мог получить доступ к каждому переключателю по отдельности, я мог бы просто изменить его состояние, чтобы оно было равно «Down». До сих пор мне удалось только найти, какую строку выбрал пользователь (хотя и не так элегантно). Мне удалось это, подсчитав количество кнопок в строке и сравнив его с индексом выбранной кнопки.

Вот пример, где я экспериментирую, обычно я предпочитаю использовать файл .kv:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.recycleview.views import RecycleDataViewBehavior


class MyButton(RecycleDataViewBehavior, ToggleButton):
    index = None

    def refresh_view_attrs(self, rv, index, data):
        """ Catch and handle the view changes """
        self.index = index
        return super(MyButton, self).refresh_view_attrs(
            rv, index, data)


class TestRecycleView(RecycleView):
    items_per_row = 3
    selected_data = None
    selected_row = None

    def find_row(self, instance):
        self.selected_row = instance.index // self.items_per_row + 1
        print('Row: ', self.selected_row)
        self.selected_data = self.data[(self.selected_row - 1) * self.items_per_row: self.items_per_row
                                       * self.selected_row]
        print('Data: ', self.selected_data)


KV = '''

<MyButton>:
    on_release:
        app.root.find_row(self)

TestRecycleView:
    data: [{'text': str(x)} for x in range(21)]
    viewclass: 'MyButton'
    id: rv_controller
    target_id: None
    RecycleGridLayout:
        cols: 3
        default_size_hint: 1, None
        orientation: 'vertical'
        key_selection: 'selectable'
        default_size: None, dp(26)
        size_hint_y: None
        height: self.minimum_height
        multiselect: True
        touch_multiselect: True

'''


class Test(App):
    def build(self):
        root = Builder.load_string(KV)
        # root.data = items
        return root


Test().run()

Поскольку мне удалось рассчитать, какая строка была выбрана пользователем, я также могу рассчитать, какие данные в RecycleView выбираются. Тем не менее, я хотел бы аккуратное визуальное представление для такого выбора строк.

Был бы очень признателен за помощь.


person Frankster    schedule 01.02.2019    source источник


Ответы (1)


Вы можете добавить хак в свой метод find_row(), чтобы установить состояние всех кнопок в строке. Этот метод устанавливает их в соответствии с состоянием выбранного экземпляра Button, поэтому вы также можете отменить выбор.

def find_row(self, instance):
    rgl = self.ids.gl
    num_buttons = len(rgl.children)
    num_rows = num_buttons // self.items_per_row
    self.selected_row = instance.index // self.items_per_row + 1
    print('Row: ', self.selected_row)
    self.selected_data = self.data[(self.selected_row - 1) * self.items_per_row: self.items_per_row
                                   * self.selected_row]
    print('Data: ', self.selected_data)
    index1 = (num_rows - self.selected_row + 1) * self.items_per_row - 1
    index2 = index1 - self.items_per_row
    state = instance.state
    for index in range(index1, index2, -1):
        rgl.children[index].state = state

Это также требует установки идентификатора RecycleGridLayout как:

id: gl

Buttons являются дочерними элементами RecycleGridLayout, поэтому их можно использовать для получения общего числа Buttons. И индекс в списке children противоположен тому, что вы ожидаете. То есть Button с меткой 0 является индексом 20, а Button с меткой 20 является индексом 0. Таким образом, вычисление index1 использует это знание для определения индекса самого левого Button в выбранной строке. Тогда самым правым Button в выбранной строке будет index - 2. range(inde1, index2, -1) проходит через Buttons в выбранной строке. Поскольку find_row() называется on_release, state из instance уже переключено, поэтому его state можно использовать для установки state из другого Buttons в строке.

person John Anderson    schedule 03.02.2019
comment
Окончательно! Я долго искал решение. Не могли бы вы написать краткое изложение того, почему это работает. Похоже, метод .children здесь ключевой. - person Frankster; 04.02.2019
comment
Еще раз привет, предложенное решение отлично работало, пока я не начал обновлять recycleview. Если я изменяю какие-либо данные и хочу обновить весь recycleview, я использую эти методы: root = App.get_running_app().root и root.refresh_from_data(). Однако, кажется, что refresh_from_data() переключает индексы переключателя. Индекс 0 переключается с верхнего левого угла на нижний правый. Это создает странное поведение, когда щелчок по строке 2 будет переключаться между выбором строки 2 и строки 2 снизу вверх. Какие-нибудь мысли? - person Frankster; 11.02.2019
comment
Я думаю, что переработка вызывает этот эффект. Когда происходит refresh_from_data(), экземпляры MyButton перерабатываются, так что метка 0, скорее всего, будет на другом экземпляре MyButton после обновления. Но state переработанных экземпляров не изменяется, поэтому выделены разные кнопки. Вы можете добавить 'state': 'normal' к каждому элементу вашего data, тогда каждое обновление будет восстанавливать состояние normal для каждой кнопки. Если вы хотите сохранить down состояние выбранных кнопок, вам придется отслеживать выбранные кнопки и сбрасывать их состояние после каждого обновления. - person John Anderson; 11.02.2019
comment
Да, это тоже была моя идея, но я обращаюсь к каждой кнопке через ее индекс, который постоянно меняется. Это означает, что при каждом втором обновлении я должен отслеживать, как индексы отражались в среднем ряду. Допустим, я нажимаю первую строку. Затем я добавляю некоторые данные и хочу обновить. Теперь я должен отслеживать тот факт, что первая строка теперь имеет индекс последней строки. Это действительно кажется мне ошибкой. Почему кто-то хочет, чтобы переработка вел себя таким образом? Могу ли я обойти все это, обращаясь к каждой кнопке через уникальный идентификатор или что-то в этом роде? Еще раз спасибо за то, что занимаетесь со мной! - person Frankster; 11.02.2019