Search code examples
python-3.xkivykivymd

KivyMD using main for events and kv file for layout


I dont want to do this:

class SmashLadderApp(MDApp):
    def build(self):
        return Builder.load_file("View.kv")

but something like this:

class WindowManager(ScreenManager): 
    pass
#kv =Builder.load_file("View.kv")
#add screen widgits and load file
wm = WindowManager()
screens = [LoginPage(name="LoginPageID"),LadderPage(name="LadderPageID"), RegisterPage(name ="RegisterPageID")]
for screen in screens:
    wm.add_widget(screen)

#starting page
wm.current = "LoginPageID"

class SmashLadderApp(MDApp):
    def build(self):
        kv =Builder.load_file("View.kv")       
        return wm

SmashLadderApp().run()

The bottom returns a blank window. How do I specify a Builder then have the widgits layout in the kv file? I want to handle the events in main. The commented outkv =Builder.load_file("View.kv") in the second snippet is how I thought you could accomplish this but gives me an error.

I'm pretty sure this works in Kivy but not KivyMD. Code pasted after as well. For the sake of readability, the code is posted here:

Python (file_name= Display.py): https://paste.nextcord.dev/?id=1659041664901348

KV file (file_name= View.kv): https://paste.nextcord.dev/?id=1659041570568365

Display.py:

from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen

#All functions/method handles
class LoginPage(Screen):
     
    def register(self):
        wm.current="RegisterPageID"

class LadderPage(Screen):
    
    def user_page(self):
        pass

    def mod_page(self):
        pass

    def dev_page(self):
        pass
class RegisterPage(Screen):
    pass

class WindowManager(ScreenManager): 
    pass

#kv =Builder.load_file("View.kv")                     
#add screen widgits and load file
wm = WindowManager()
screens = [LoginPage(name="LoginPageID"),LadderPage(name="LadderPageID"), RegisterPage(name ="RegisterPageID")]
for screen in screens:
    wm.add_widget(screen)

#starting page
wm.current = "LoginPageID"
class SmashLadderApp(MDApp):
    def build(self):
        kv =Builder.load_file("View.kv")              
        return wm

SmashLadderApp().run()

View.kv:

#:kivy 2.1
# WindowManager:
#     LoginPage:
#     RegisterPage:
#     LadderPage:
#Default widgits, almost like methods
<MDLabel>:
    size_hint: .2,.1
    color: 0,0,0,1
    bold: True
<MDTextField>:
    hint_text_color_normal:1,1,1,1
    # color_mode: "custom"
    hint_text_color_focus: 1,1,1,1
    helper_text_color_focus: 1,1,1,1
    line_color_normal: 1,1,1,1
    line_color_focus: 1,1,1,1
    # icon_right: "KirbyHeart.ico"
<MDRectangleFlatButton>:
    theme_text_color: "Custom"
    text_color: 1,1,1,1
    line_color: 1,1,1,0
#pages
<LoginPage>:
    name: "LoginPageID"
    #create opacity
    canvas:
        Color:
            rgba: 0.1,0.1,0.1,.8
        #creates background
        Rectangle:
            source: "ssbu.jpg"
            pos: self.pos
            size: self.size

    FloatLayout:
        #Places kirby heart in certain location, don't know how to make Kirby stay next to text input
        Image: 
            source:"KirbyHeart.ico"
            pos_hint: {"center_x": .65, "center_y": .50}

        #just a label
        MDLabel:
            #can use mark up language
            markup: True
            text: "[font=times]Smash    Ladder\n        Login[/font]"                              
            font_size: 20
            #relative size to window
            pos_hint:{"center_x":.52,"center_y": .58}
            color: "white"

        MDTextField:
            id: usernameID
            hint_text: "please enter your username"   
            helper_text: "Click register if you haven't yet"
            helper_text_mode:"on_focus"
            pos_hint:{"center_x":.5, "center_y":.5}
            size_hint_x: None
            width: 200
        MDTextField:
            id: passwordID
            hint_text: "Enter your password. "
            pos_hint:{"center_x":.5, "center_y":.4}
            size_hint_x: None
            width: 200
        MDRectangleFlatButton:
            text: "Register?"
            text_color: 0,0,1,1
            pos_hint:{"center_x":.595, "center_y":.35}
            size_hint_x: None
            on_press:
                root.manager.transition.direction: "up"
                root.register()
            on_release:
                

<LadderPage>:
    name: "LadderPageID"

<RegisterPage>:
    #Testing Layout
    name: "RegisterPageID"
    FloatLayout:
        Image: 
            source:"KirbyHeart.jpg"
            pos_hint: {"center_x": .5, "center_y": .5}
        MDRectangleFlatButton:
            id: jokeID
            text: "Register?"
            text_color: 0,0,1,1
            pos_hint:{"center_x":.59

if I remove Builder object from the build method and insert it below the WindowManager class; I will get this error:

Traceback (most recent call last):
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivymd\theming.py", line 1216, in __init__
     App.get_running_app().property("theme_cls", True),
 AttributeError: 'NoneType' object has no attribute 'property'

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "c:\Users\Chapm\GitHub\Ladder-matchmaking\UI\Display.py", line 30, in <module>
     screens = [LoginPage(name="LoginPageID"),LadderPage(name="LadderPageID"), RegisterPage(name ="RegisterPageID")]
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\relativelayout.py", line 274, in __init__
     super(RelativeLayout, self).__init__(**kw)
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\floatlayout.py", line 65, in __init__
     super(FloatLayout, self).__init__(**kwargs)
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\layout.py", line 76, in __init__
     super(Layout, self).__init__(**kwargs)
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\widget.py", line 366, in __init__
     self.apply_class_lang_rules(
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\widget.py", line 470, in apply_class_lang_rules
     Builder.apply(
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\lang\builder.py", line 540, in apply
     self._apply_rule(
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\lang\builder.py", line 662, in _apply_rule
     self._apply_rule(
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\lang\builder.py", line 658, in _apply_rule
     child = cls(__no_builder=True)
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivymd\uix\label\label.py", line 327, in __init__
     super().__init__(**kwargs)
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivymd\uix\behaviors\declarative_bahavior.py", line 302, in __init__
     super().__init__(**kwargs)
   File "C:\Users\Chapm\AppData\Local\Programs\Python\Python310\lib\site-packages\kivymd\theming.py", line 1224, in __init__
     raise ValueError(
 ValueError: KivyMD: App object must be initialized before loading root widget. See https://github.com/kivymd/KivyMD/wiki/Modules-Material-App#exceptions

Solution

  • Just move everything to build as,

        def build(self):
            Builder.load_file("View.kv") # You can place it outside too.
            wm = WindowManager()
            screens = [LoginPage(name="LoginPageID"),LadderPage(name="LadderPageID"), RegisterPage(name ="RegisterPageID")]
            for screen in screens:
                wm.add_widget(screen)
            return wm
    

    Remember now, wm is not global anymore, so something like this wm.some_prop_or_method will raise NameError.