Search code examples
pythonkivy

Python Kivy widgets duplicated on top of each other on a custom app restart method


Basically I'm working on a Kivy app that consists of multiple screens. Recently I decided to give my app a little Refresh button that refreshes the widgets and reread the savefile for sorta debugging purpose, and then I of course stole the code from this question's answer, like how every programmer would do. It worked, but there's one slight problem: every time I press the refresh button, for whatever reason the widgets got duplicated and placed on top of each other(even though the restart function cleared the former widgets beforehand). Here's my code, simplified:

test.py

from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivy.config import Config


Config.set("kivy", "exit_on_escape", 0)


class First(Screen):
    pass


class Second(Screen):
    pass


class Test(MDApp):
    def restart(self):

        self.root.clear_widgets()
        self.stop()
        Test().run()


Test().run()

test.kv

ScreenManager:
    First:
    Second:
    
<First>:
    MDFloatLayout:

        MDTextField:
            size_hint: 0.8, 0.2
            pos_hint: {'center_x': 0.5, 'top': 0.9}
            hint_text: 'owo uwu test im a furry yes'

        MDRectangleFlatButton:
            text: 'Restart'
            size_hint: 0.15, 0.1
            pos_hint: {'center_x': 0.5, 'center_y': 0.5}
            on_release: app.restart()

<Second>:

Also note that the reason to why I used screens in this example app although there's no way to access the Second screen, is because I did some tests and the result is that the "bug" only occurs with the ScreenManager being the root class, others like FloatLayout works properly. Secondly the reason why I used KivyMD in this example is that it's easier to detect the duplication of widgets using MDTextField because of the animation of the hint text.

Does anyone know what's the cause behind the problem, as well as the solution to this? Any help would be appreciated.

Edit: Here are images for demonstration:

Before restarting:

Before restarting

After restarting:

After restarting


Solution

  • Th problem is that your test.kv file is getting loaded twice due to Kivy doing its automatic loading based on the file name. The fix is to control the kv file loading yourself. To do that, change the name of your kv file to anything other than test.kv, perhaps not_test.kv. Then create a build() method in your Test App:

    class Test(MDApp):
        def build(self):
            from kivy.resources import resource_find
    
            filename = 'not_test.kv'
            filename = resource_find(filename) or filename
            if filename in Builder.files:
                Builder.unload_file(filename)
            return Builder.load_file(filename)
    

    This code unloads the kv file if it has already been loaded, then loads it again in order to get the root widget.