Python-kivy: почему цикл for отличается от цикла while при удалении элементов в kivy? - PullRequest
0 голосов
/ 25 августа 2018

Я заметил разницу между циклом for и циклом while при попытке удалить элементы из макета. Вот небольшой код для иллюстрации:

test.kv

BoxLayout:
    orientation: 'vertical'
    spacing: 20

    BoxLayout:
        orientation: 'vertical'
        spacing: 20
        id : box
        Button:
            text: 'BUTTON1'

        Button:
            text: 'BUTTON2'

        Button:
            text: 'BUTTON3'

    Button:
        text: 'BUTTON'
        size_hint: None, None
        size: dp(100), dp(50)
        pos_hint: {'center_x': 0.5, 'center_y': 0.1}
        on_release: app.del_button()

С циклом for: main.py

from kivy.app import App

class TestApp(App):

    def del_button(self):
        children = self.root.ids.box.children
        for i in children:
            self.root.ids.box.remove_widget(i)

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

В этом первом случае, когда я нажимаю кнопку «BUTTON», первая и третья кнопки удаляются, но кнопка «BUTTON2» остается видимой.

С циклом while: main.py

from kivy.app import App

class TestApp(App):

    def del_button(self):
        children = self.root.ids.box.children
        i = 0
        while i < len(children):
            self.root.ids.box.remove_widget(children[i])

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

Во втором случае все кнопки удаляются напрямую.

В цикле for не все элементы удаляются в первый раз, в то время как цикл while хорошо справляется со своей задачей. Почему это странное поведение?

Заранее благодарю за ответы.

Ответы [ 2 ]

0 голосов
/ 26 августа 2018

для цикла

1-я итерация

До цикла for, children содержит список из трех областей памяти виджетов кнопок, [Button3, Button2, Button1].

В первой итерации i ссылается на первый элемент в children, и он удалил виджет Button3.

2-я итерация

Перед второй итерацией, children содержит список из двух областей памяти виджетов кнопок, [Button2, Button1].

Во второй итерации я ссылаюсь на второй элемент в children, и он удалил виджет Button1.

3-я итерация

Перед третьей итерацией, children содержит список из двух мест в памяти виджетов кнопок, [Button2].

Поскольку в children нет третьего элемента, цикл for завершается.

Пока цикл

Поскольку i (индекс) всегда остается на 0, следовательно, он успешно удалил все виджеты с тремя кнопками, поскольку он всегда ссылается на первый элемент в списке, children. self.root.ids.box.remove_widget(children[i]) эквивалентно self.root.ids.box.remove_widget(children[0])

Решение

Решением для цикла for является использование reversed(self.root.ids.box.children).

Отрывок

def del_button(self):
    for child in reversed(self.root.ids.box.children):
        self.root.ids.box.remove_widget(child)
0 голосов
/ 25 августа 2018

Оператор for создает iterator для использования в цикле.iterator создается в начале цикла (см. для документации цикла ), поэтому он может быть неправильным, если вы изменили список после начала цикла.

Цикл while, однако, не использует iterator и не будет смущен изменяющейся структурой списка.Фактически, более простая реализация вашего del_button() метода может быть:

def del_button(self):
    children = self.root.ids.box.children
    while len(children) > 0:
        self.root.ids.box.remove_widget(children[0])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...