Csaba Horvath
Csaba Horvath

Reputation: 73

When clear_widgets is called, it doesnt remove screens in ScreenManager

I have a nested screen manager in my Kivy gui app and both of them loads next and previuos screens and they work absolutly fine.

I have a button which reloads every screen again and not to duplicate things i have to remove all the screens first and then recall the function which rebuilds all the screens and widgets inside them.

but whenever i do some, screens are not deleted. With help of all current screen names print(self.screen_names) i could see what happens to the screens each time i push the button.

When button is not pushed screen_names: ['Screen 1', 'Screen 2', 'Screen 3', 'Screen 4']

after i push the button screen_names: ['Screen 2', 'Screen 4', 'Screen 1', 'Screen 2', 'Screen 3', 'Screen 4']

second push screen_names: ['Screen 4', 'Screen 2', 'Screen 4', 'Screen 1', 'Screen 2', 'Screen 3', 'Screen 4']

third push screen_names: ['Screen 2', 'Screen 1', 'Screen 3', 'Screen 1', 'Screen 2', 'Screen 3', 'Screen 4']

after 3rd push i always get this output: ['Screen 1', 'Screen 1', 'Screen 3', 'Screen 1', 'Screen 2', 'Screen 3', 'Screen 4'] it repeats itself

And switching screens gets messed up because the manager doesnt which screen to witch to.

py:

class Manager(ScreenManager):
    def __init__(self, **kwargs):
        super(Manager, self).__init__(**kwargs)
        Clock.schedule_once(self.pst_nt, 0)

    def refresh(self):
        print(self.screen_names)
        self.pst_nt(None)
        self.current = 'Screen 1'


    def next_s(self):
        self.current = self.next()
        self.transition.direction = 'left'

    def previous_s(self):
        self.current = self.previous()
        self.transition.direction = 'right'

    def pst_nt(self, dt):
        self.clear_widgets()
        dn.load()
        screen_number = 0
        for k, v in dn.DND.items():
            screen_number += 1
            print(screen_number)
            self.bxlt = BoxLayout(size_hint=(1, .1),

I tried remove_widget() but there i get a completely different error.

Edit1: A minimal runnable example:

from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen

Dict = {'rand1': 1, 'rand2':2, 'rand3':3}

class Manager(ScreenManager):
    def __init__(self, **kwargs):
        super(Manager, self).__init__(**kwargs)
        Clock.schedule_once(self.pst_nt, 0)

    def refresh(self):
        print(self.screen_names)
        self.pst_nt(None)
        self.current = 'Screen 1'


    def next_s(self):
        self.current = self.next()
        self.transition.direction = 'left'

    def previous_s(self):
        self.current = self.previous()
        self.transition.direction = 'right'

    def pst_nt(self, dt):
        self.clear_widgets()
        #dn.load()        # this is needed for my project
        screen_number = 0
        for k, v in Dict.items():
            screen_number += 1
            self.mainlbl = Label(text='{}  {}'.format(screen_number, str(k)),
                                       font_size='13sp',
                                       size_hint=(.5, 1))
            self.refbtn = Button(text='Refresh',
                                 size_hint=(1, .3),
                                 on_release=lambda x: self.refresh())
            self.next_btn = Button(text='Next',
                                   size_hint=(1, .2),
                                   on_release=lambda x: self.next_s())
            self.back_btn = Button(text='Back',
                                   size_hint=(1, .2),
                                   on_release=lambda x: self.previous_s())
            self.bxlt = BoxLayout(orientation= 'vertical',size_hint=(1, 1))

            self.bxlt.add_widget(self.mainlbl)
            self.bxlt.add_widget(self.refbtn)
            self.bxlt.add_widget(self.next_btn)
            self.bxlt.add_widget(self.back_btn)

            self.scrn = Screen(name='Screen {}'.format(screen_number))

            self.scrn.add_widget(self.bxlt)
            self.add_widget(self.scrn)

class TestApp(App):
    def build(self):
        return Manager()

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

So here in this example after i push the refresh button, i cant load some screens.

Upvotes: 1

Views: 505

Answers (1)

inclement
inclement

Reputation: 29450

You've discovered a bug in Kivy, ScreenManager.clear_widgets doesn't correctly iterate over the screens.

I've created a pull request to fix this here. It should be included in the next Kivy release.

In the meantime, you can work around the problem by overriding clear_widgets yourself in your ScreenManager subclass:

def clear_widgets(self, screens=None):
    if screens is None:
        screens = self.screens

    for screen in screens[:]:
        self.remove_widget(screen)

This is the same as the fix I pushed above.

Upvotes: 1

Related Questions