0

I'm facing a problem with my Kivy application where each time I open the same screen/page twice, the widgets (labels and checkboxes) overlap, causing a messy and undesirable user interface. It seems that Kivy is keeping all instances of the screen, leading to this issue.

I have a custom screen class named SP_list_Screen, which inherits from the Kivy Screen class. The class creates a vertical BoxLayout and adds labels and checkboxes to it. However, when I navigate to the same screen multiple times, the widgets from previous instances persist, causing the overlapping problem.

Is there a way to prevent Kivy from keeping all the instances of the screen, or is there a way to ensure that the widgets are cleared when navigating to the screen again?

Here is the two classes i've been working with in main.py:

class SpotifyScreen(Screen):

    def sp_update_btn_txt(self):
        self.children[1].text = "Checking..."
        self.ids.sp_songview.text = ""
        print(sm.screen_names)
        if 'sp_list' in sm.screen_names:
            sm.remove_widget(sm.get_screen('sp_list'))
        sm.add_widget(SP_list_Screen(name='sp_list'))
        Clock.schedule_once(lambda dt: self.link_to_details1(), 0.1)

    def link_to_details1(self):
        path = self.ids.sp_path_input.text
        if path == "":
            path = "Downloads"
        link = self.ids.sp_text_input.text
        if "track" in link:
            names = songname(link)
            self.ids.sp_songview.text = names
            self.children[1].text = "Check"
        else:
            names=get_song_names(link)
            self.manager.get_screen('sp_list').list_update(names)
            self.manager.current = 'sp_list'
            self.children[1].text = "Check"
            


class SP_list_Screen(Screen):
    name = "sp_list"
    layout = BoxLayout(orientation='vertical')

    def list_update(self, my_list):
        self.layout.clear_widgets()
        self.my_list = my_list[1:]
        self.heading=my_list[0]
        self.b_l()
    
    def b_l(self):
        layout = BoxLayout(orientation='vertical')
        top_padding_layout = BoxLayout(orientation='horizontal', size_hint=(1, 0.1))
        icon_button = MDIconButton(icon='arrow-left', on_release=self.back)
        label = Label(text=self.heading, halign='left',color=(0, 0, 0, 1),text_size=(350,None))
        top_padding_layout.add_widget(icon_button)
        top_padding_layout.add_widget(label)
        layout.add_widget(top_padding_layout)
        scrollview = ScrollView()
        self.selected_songs = []
        grid_layout = GridLayout(cols=1, spacing=dp(20), size_hint_y=None, padding=dp(5))
        grid_layout.bind(minimum_height=grid_layout.setter('height'))
        self.checkboxes = []
        self.list_display(grid_layout, scrollview, layout)
    
    def list_display(self, grid_layout, scrollview, layout):

        for i in range (len(self.my_list)):
            checkbox_layout = BoxLayout(orientation='horizontal', size_hint=(1, None), height=dp(30))
            label = Label(text=self.my_list[i], size_hint=(0.7, None), color=(0, 0, 0, 1),height=dp(30),text_size=(250,None))
            checkbox = CheckBox(size_hint=(0.3, None), height=dp(30), color=(0, 0, 0, 1))
            checkbox.label = self.my_list[i]
            checkbox.active = False
            checkbox.bind(active=self.on_checkbox_active)
            checkbox_layout.add_widget(label)
            checkbox_layout.add_widget(checkbox)
            self.checkboxes.append(checkbox)
            grid_layout.add_widget(checkbox_layout)
        self.a_l(scrollview, grid_layout, layout)
        
    def a_l(self, scrollview, grid_layout, layout):
        scrollview.add_widget(grid_layout)
        layout.add_widget(scrollview)

        download_button = MDRectangleFlatButton(text='Download Selected', size_hint=(1, 0.1), on_press=self.download_selected_songs)
        layout.add_widget(download_button)
        self.add_widget(layout)

    def on_checkbox_active(self, checkbox, value):
        song_name = checkbox.label
        if value:
            self.selected_songs.append(song_name)
        else:
            self.selected_songs.remove(song_name)

    def back(self,instance):
        self.manager.current = 'spotify'

    def download_selected_songs(self, instance):
        print("Selected Songs:")
        print(self.selected_songs)

And here is the classes in .kv file i've been working with:

<SpotifyScreen>:
    name: 'spotify'
    Image:
        source: 'projects\Photos\spotify_logo.png'
        size_hint: None,None
        size: 250,250
        pos_hint: {'center_x': 0.5, 'center_y': 0.87}
    MDIconButton:
        icon: "arrow-left"
        on_release: root.manager.current = 'Home'
        pos_hint: {'center_x': 0.05, 'center_y': 0.96}
    TextInput:
        id: sp_text_input
        hint_text: "Enter Spotify link here"
        size_hint: (0.8, 0.05)
        pos_hint: {"center_x": 0.5, "center_y": 0.7}
        multiline: False
    TextInput:
        id: sp_path_input
        hint_text: "Enter Your Download Path Here"
        size_hint: (0.8, 0.05)
        pos_hint: {"center_x": 0.5, "center_y": 0.15}
        multiline: False
    MDRectangleFlatButton:
        text: 'Check Details'
        pos_hint: {"center_x": 0.5, "center_y": 0.6}
        on_press: root.sp_update_btn_txt()
    Label:
        id: sp_songview
        text: ""
        font_size: 18
        text_size: (410, None)
        color: 0, 0, 0, 1 
        pos_hint: {'center_x': 0.5, 'center_y': 0.5}

I haven't made a class for the SP_list_Screen in my kv file since most of it is depending on the user. i used the main.py for this part.

here is the images :

for the first time when it runs, everything works fine and well in this case

This is when i open the same screen or page of the app twice without closing the app. more like if i go back to the previous page and come back to the same page

I would greatly appreciate any guidance or fixes for this issue. Thank you!

i have tried deleting the screen itself and cretaing it everytime it gets called and it doesn't work.

i'm expecting any fixes available for this issue.

Here is a minimal reproducible example for references:

from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivymd.uix.button import MDRectangleFlatButton
from kivy.metrics import dp
from kivymd.uix.button import MDIconButton




class SP_list_Screen(Screen):
    name = "sp_list"
    layout = BoxLayout(orientation='vertical')

    def list_update(self, my_list):
        self.layout.clear_widgets()
        self.my_list = my_list[1:]
        self.heading=my_list[0]
        self.b_l()
    
    def b_l(self):
        layout = BoxLayout(orientation='vertical')
        top_padding_layout = BoxLayout(orientation='horizontal', size_hint=(1, 0.1))
        icon_button = MDIconButton(icon='arrow-left', on_release=self.back)
        label = Label(text=self.heading, halign='left',color=(0, 0, 0, 1),text_size=(350,None))
        top_padding_layout.add_widget(icon_button)
        top_padding_layout.add_widget(label)
        layout.add_widget(top_padding_layout)
        scrollview = ScrollView()
        self.selected_songs = []
        grid_layout = GridLayout(cols=1, spacing=dp(20), size_hint_y=None, padding=dp(5))
        grid_layout.bind(minimum_height=grid_layout.setter('height'))
        self.checkboxes = []
        self.list_display(grid_layout, scrollview, layout)
    
    def list_display(self, grid_layout, scrollview, layout):

        for i in range (len(self.my_list)):
            checkbox_layout = BoxLayout(orientation='horizontal', size_hint=(1, None), height=dp(30))
            label = Label(text=self.my_list[i], size_hint=(0.7, None), color=(0, 0, 0, 1),height=dp(30),text_size=(250,None))
            checkbox = CheckBox(size_hint=(0.3, None), height=dp(30), color=(0, 0, 0, 1))
            checkbox.label = self.my_list[i]
            checkbox.active = False
            checkbox.bind(active=self.on_checkbox_active)
            checkbox_layout.add_widget(label)
            checkbox_layout.add_widget(checkbox)
            self.checkboxes.append(checkbox)
            grid_layout.add_widget(checkbox_layout)
        self.a_l(scrollview, grid_layout, layout)
        
    def a_l(self, scrollview, grid_layout, layout):
        scrollview.add_widget(grid_layout)
        layout.add_widget(scrollview)

        download_button = MDRectangleFlatButton(text='Download Selected', size_hint=(1, 0.1), on_press=self.download_selected_songs)
        layout.add_widget(download_button)
        self.add_widget(layout)

    def on_checkbox_active(self, checkbox, value):
        song_name = checkbox.label
        if value:
            self.selected_songs.append(song_name)
        else:
            self.selected_songs.remove(song_name)

    def back(self,instance):
        self.manager.current = 'my_button'

    def download_selected_songs(self, instance):
        print("Selected Songs:")
        print(self.selected_songs)


class MyButtonScreen(Screen):
    def __init__(self, **kwargs):
        super(MyButtonScreen, self).__init__(**kwargs)
        self.name = "my_button"
        self.layout = MDRectangleFlatButton(text="Click Me",on_press=self.On_press)
        self.add_widget(self.layout)
    def On_press(self,instance):
        names=['Space Song - song and lyrics by Beach House ', 'Myth - song and lyrics by Beach House ', 'Apocalypse - song and lyrics by Cigarettes After Sex ', 'Fourth of July - song and lyrics by Sufjan Stevens ', 'Cry - song and lyrics by Cigarettes After Sex ', 'Touch - song and lyrics by Cigarettes After Sex ', 'On the Sea - song and lyrics by Beach House ', 'Somewhere Only We Know - song and lyrics by Keane ', 'Here With Me - song and lyrics by d4vd ', 'The Night We Met - song and lyrics by Lord Huron ', "You're Somebody Else - song and lyrics by flora cash ", "You're All I Want - song and lyrics by Cigarettes After Sex ", 'Until I Found You - song and lyrics by Stephen Sanchez ', 'Glimpse of Us - song and lyrics by Joji ', 'Repeat Until Death - song and lyrics by Novo Amor ', 'Another Love - song and lyrics by Tom Odell ', 'In My Head - song and lyrics by Bedroom ', 'Past Lives - song and lyrics by Farizki ']
        self.manager.get_screen('sp_list').list_update(names)
        self.manager.current = 'sp_list'



class MyApp(MDApp):
    def build(self):
        Window.size=(340,600)
        sm = ScreenManager()
        sm.add_widget(MyButtonScreen(name="button_screen"))
        sm.add_widget(SP_list_Screen(name='sp_list'))
        return sm

if __name__ == '__main__':
    MyApp().run()
  • Please post a [mcve]. – John Anderson Jul 30 '23 at 15:20
  • @john-anderson as you asked i've added a minimal reproducible example at the end of the original post. please look through and let me know what mistakes i'm actually making here. Let me know if you need anything else. – Avijit Bhuin Aug 01 '23 at 19:34

1 Answers1

0

In your list_update() method you are clearing self.layout, but self.layout is not actually used in your GUI, so nothing is actually cleared. Then all the widgets are added again, making the overlapping. The fix is to actually use self.layout:

def list_update(self, my_list):
    self.layout.clear_widgets()
    self.my_list = my_list[1:]
    self.heading = my_list[0]
    self.b_l()

And also use the reference to self.layout in b_l():

def b_l(self):
    self.layout = BoxLayout(orientation='vertical')
    top_padding_layout = BoxLayout(orientation='horizontal', size_hint=(1, 0.1))
    icon_button = MDIconButton(icon='arrow-left', on_release=self.back)
    label = Label(text=self.heading, halign='left', color=(0, 0, 0, 1), text_size=(350, None))
    top_padding_layout.add_widget(icon_button)
    top_padding_layout.add_widget(label)
    self.layout.add_widget(top_padding_layout)
    scrollview = ScrollView()
    self.selected_songs = []
    grid_layout = GridLayout(cols=1, spacing=dp(20), size_hint_y=None, padding=dp(5))
    grid_layout.bind(minimum_height=grid_layout.setter('height'))
    self.checkboxes = []
    self.list_display(grid_layout, scrollview, self.layout)
John Anderson
  • 35,991
  • 4
  • 13
  • 36
  • Thanks a lot @JohnAnderson, it works as expected. somehow i overlooked the fact that i'm making a new layout in b_l function. Thanks again. – Avijit Bhuin Aug 02 '23 at 06:03