首页 > 解决方案 > KivyMD:AttributeError:“NoneType”对象在添加 MDToolbar 时没有属性“theme_cls”

问题描述

我正在使用KivyKivyMD,我正在尝试实现MDToolbar。运行我的程序后,我收到此错误:AttributeError: 'NoneType' object has no attribute 'theme_cls'. 我只包含了我的程序中的相关代码。在添加MDToolbar之前,一切正常。我怎样才能让MDToolbar工作?请帮忙!

我的完整错误:

Parser: File "<inline>", line 22:
...
     20:<MDToolbar>
     21:    size_hint_y: None
>>   22:    height: root.theme_cls.standard_increment
     23:    padding: [root.theme_cls.horizontal_margins - dp(12), 0]
     24:    opposite_colors: True
...
AttributeError: 'NoneType' object has no attribute 'theme_cls'
  File "C:\Users\kelleym\AppData\Local\Continuum\anaconda3\lib\site-packages\kivy\lang\builder.py", line 696, in _apply_rule
    setattr(widget_set, key, value)
  File "kivy\weakproxy.pyx", line 35, in kivy.weakproxy.WeakProxy.__setattr__
  File "kivy\properties.pyx", line 497, in kivy.properties.Property.__set__
  File "kivy\properties.pyx", line 544, in kivy.properties.Property.set
  File "kivy\properties.pyx", line 599, in kivy.properties.Property.dispatch
  File "kivy\_event.pyx", line 1214, in kivy._event.EventObservers.dispatch
  File "kivy\_event.pyx", line 1120, in kivy._event.EventObservers._dispatch
  File "kivy\properties.pyx", line 1318, in kivy.properties.ReferenceListProperty.trigger_change
  File "kivy\properties.pyx", line 1333, in kivy.properties.ReferenceListProperty.trigger_change
  File "kivy\properties.pyx", line 599, in kivy.properties.Property.dispatch
  File "kivy\_event.pyx", line 1214, in kivy._event.EventObservers.dispatch
  File "kivy\_event.pyx", line 1120, in kivy._event.EventObservers._dispatch
  File "C:\Users\kelleym\AppData\Local\Continuum\anaconda3\lib\site-packages\kivymd\uix\behaviors\elevation.py", line 105, in _update_shadow
    self._shadow = App.get_running_app().theme_cls.quad_shadow
  File "C:\Users\kelleym\Desktop\Actual Inventory App\main.py", line 313, in <module>
    sm.add_widget(inventory(name='inventory'))

我的代码:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.theming import ThemeManager

Builder.load_string("""
<inventory>:
    NavigationLayout:
        id: "nav_layout"
        MDToolbar:
            title: "test"
        MDNavigationDrawer:
            drawer_logo: "test.png"
            id: "nav_drawer"
            NavigationDrawerSubheader:
                text: "[color=#black]Categories[/color]"
            NavigationDrawerIconButton:
                icon: 'printer'
                text: "Printers"
                on_release: root.manager.current = 'printers'
            NavigationDrawerIconButton:
                text: "PCs"
                icon: "laptop"
            NavigationDrawerIconButton:
                text: "Monitors"
                icon: "monitor"
            NavigationDrawerIconButton:
                text: "Tablets"
                icon: "tablet"
            NavigationDrawerIconButton:
                text: "Non-HP"
                icon: "close-circle-outline"
            NavigationDrawerIconButton:
                text: "Supplies"
                icon: "water"
            NavigationDrawerIconButton:
                text: "Misc."
                icon: "paperclip"
        Button:
            text: "test"
            on_release: app.nav_drawer.toggle()
""")


class inventory(BoxLayout, Screen):
    pass


sm = ScreenManager()
sm.add_widget(inventory(name='inventory'))


class TestApp(App):
    theme_cls = ThemeManager()

    def build(self):
        return sm


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

标签: pythonkivykivy-language

解决方案


All code here works with Kivy 1.11.1 and KivyMD 0.102.0. KivyMD is in alpha status and API can be changed in future.

  1. Create all widgets inside build function. You should move creating of ScreenManager:
    def build(self):
        sm = ScreenManager()
        sm.add_widget(inventory(name='inventory'))
        return sm
  1. NavigationLayout cannot take more than two widgets, one of them is MDNavigationDrawer and second is layout. Create BoxLayout and put in it MDToolbar and Button:
    NavigationLayout:
        MDNavigationDrawer:
            # ...
            NavigationDrawerSubheader:
                # ...
        BoxLayout:
            orientation: "vertical"
            MDToolbar:
                title: "test"
            Button:
                text: "test"
                on_release: app.nav_drawer.toggle()
  1. Id property in kv code should be without "":
    NavigationLayout:
        id: nav_layout
        MDNavigationDrawer:
            id: nav_drawer
  1. To call widget by id you should use app.root.ids.nav_drawer.

  2. To toggle NavigationDrawer you should call NavigationLayout: app.root.ids.nav_layout.toggle_nav_drawer().

  3. Usually NavigationLayout used as root widget. Add ScreenManager to BoxLayout.

  4. You can add screens to ScreenManager using kv code:

        ScreenManager:
            id: screen_manager

            Inventory:
                name: "inventory"
  1. Do not combine layouts. Do not write class Inventory(BoxLayout, Screen):. Create BoxLayout inside your screen:
<Inventory>:
    BoxLayout:
        orientation: "vertical"

Final code will be:

from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import Screen
from kivymd.theming import ThemeManager

root_kv = """
NavigationLayout:
    id: nav_layout

    MDNavigationDrawer:
        id: nav_drawer
        drawer_logo: "test.png"

        NavigationDrawerSubheader:
            text: "Categories"
        NavigationDrawerIconButton:
            text: "Printers"
            icon: "printer"
            on_release: app.screen_manager.current = "printers"
        NavigationDrawerIconButton:
            text: "PCs"
            icon: "laptop"
        NavigationDrawerIconButton:
            text: "Monitors"
            icon: "monitor"
        NavigationDrawerIconButton:
            text: "Tablets"
            icon: "tablet"
        NavigationDrawerIconButton:
            text: "Non-HP"
            icon: "close-circle-outline"
        NavigationDrawerIconButton:
            text: "Supplies"
            icon: "water"
        NavigationDrawerIconButton:
            text: "Misc."
            icon: "paperclip"

    BoxLayout:
        orientation: "vertical"

        ScreenManager:
            id: screen_manager

            Inventory:
                name: "inventory"


<Inventory>:
    BoxLayout:
        orientation: "vertical"

        MDToolbar:
            title: "test"

        Button:
            text: "test"
            on_release: app.root.toggle_nav_drawer()
"""


class Inventory(Screen):
    pass


class MainApp(App):
    theme_cls = ThemeManager()
    screen_manager = ObjectProperty()

    def build(self):
        self.root = Builder.load_string(root_kv)
        self.screen_manager = self.root.ids.screen_manager


if __name__ == "__main__":
    MainApp().run()

推荐阅读