Kivy:动态添加屏幕小部件时属性错误

Kivy: Attribute error when dynamically adding screen widget

我正在尝试在调用 add_project_screen 时在 ProjectScreen 动态 class 实例中创建。我想创建 ProjectScreen 的各种实例,每个实例都有不同的名称,名称是从 project_name_text_input 获得的。这是我的 .py 和 .kv 文件:

from kivy.app import App

from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.uix.listview import ListItemButton
from kivy.factory import Factory

class ProjectListButton(ListItemButton):
    pass

class HomeScreen(Screen):
    pass

class ProjectListScreen(Screen):
    project_list = ObjectProperty(None)

class ProjectScreen(Screen):
    pass


class NewProjectScreen(Screen):
    project_name_text_input = ObjectProperty(None)
    project_address_text_input = ObjectProperty(None)
    project_scope_text_input = ObjectProperty(None)


    def add_project_list_item(self,project_name_text_input):
        new_project_name = project_name_text_input.text
        project_list_screen = self.manager.get_screen('project_list_screen')
        project_list_screen.project_list.adapter.data.extend([new_project_name])
        # Reset the ListView
        project_list_screen.project_list._trigger_reset_populate()
        #project_name_text_input.text = ''

    def add_project_screen(self,project_name_text_input, project_address_text_input, project_scope_text_input):
        name = project_name_text_input.text
        s = Factory.ProjectScreen(name=name)
        self.manager.add_widget(s)

class ReportingApp(App):
    def build(self):
        screen_manager = ScreenManager()
        screen_manager.add_widget(HomeScreen(name="home_screen"))
        screen_manager.add_widget(ProjectListScreen(name="project_list_screen"))
        screen_manager.add_widget(NewProjectScreen(name="new_project_screen"))
        return screen_manager

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

这里是 .kv 文件:

#:kivy 1.10.1

#: import main main
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import ListItemButton kivy.uix.listview.ListItemButton


<HomeScreen>:
BoxLayout:
    orientation: "vertical"
    padding: 100
    spacing: 25
    Label:
        size_hint_y: None
        height: 150
        text: "Site Visit Reporting App v1.1"

    Button:
        text: "New Project"
        on_press:
            root.manager.transition.direction = 'left'
            root.manager.current = 'new_project_screen'

    Button:
        text: "Project List"
        on_press:
            root.manager.transition.direction = 'left'
            root.manager.current = 'project_list_screen'

<NewProjectScreen>:

project_name_text_input: project_name
project_address_text_input: project_address
project_scope_text_input: project_scope

BoxLayout:
    orientation: "vertical"

    Label:
        text: 'Project Name:'
    TextInput:
        id: project_name

    Label:
        text: 'Project Address:'
    TextInput:
        id: project_address

    Label:
        text: 'Project Scope:'
    TextInput:
        id: project_scope

    BoxLayout:
        padding: 15
        spacing: 25
        Button:
            text: 'OK'
            on_press:
                root.add_project_list_item(project_name)
                root.add_project_screen(project_name, project_address, project_scope)
        Button:
            text: 'Back'
            on_press:
                root.manager.transition.direction = 'right'
                root.manager.current = 'home_screen'

<ProjectListScreen>:

project_list: project_list_view

BoxLayout:
    orientation: 'vertical'
    Label:
        text: 'Current list of projects'
    Button:
        text: 'Back'
        on_press:
            root.manager.transition.direction = 'right'
            root.manager.current = 'home_screen'

    ListView:
        id: project_list_view
        adapter:
            ListAdapter(data=[], cls=main.ProjectListButton)

<ProjectScreen@Screen>:
    Label:
        text: 'hook up project info here'
    button:
        text: 'Back'

这是我的错误信息:

C:\Users\amars\AppData\Local\Programs\Python\Python37-32\python.exe C:/Users/amars/PycharmProjects/ReportingApp/main.py
[INFO   ] [Logger      ] Record log in C:\Users\amars\.kivy\logs\kivy_18-08-30_6.txt
[INFO   ] [Kivy        ] v1.10.1
[INFO   ] [Python      ] v3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit (Intel)]
[INFO   ] [Factory     ] 194 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_gif (img_pil, img_ffpyplayer ignored)
[INFO   ] [Text        ] Provider: sdl2
[WARNING] [Factory     ] Ignored class "ProjectScreen" re-declaration. Current -  module: None, cls: <class '__main__.ProjectScreen'>, baseclass: None, filename: None. Ignored -  module: None, cls: None, baseclass: Screen, filename: C:\Users\amars\PycharmProjects\ReportingApp\reportingapp.kv.
[INFO   ] [Window      ] Provider: sdl2
[INFO   ] [GL          ] Using the "OpenGL" graphics system
[INFO   ] [GL          ] GLEW initialization succeeded
[INFO   ] [GL          ] Backend used <glew>
[INFO   ] [GL          ] OpenGL version <b'4.3.0 - Build 20.19.15.4549'>
[INFO   ] [GL          ] OpenGL vendor <b'Intel'>
[INFO   ] [GL          ] OpenGL renderer <b'Intel(R) HD Graphics 4400'>
[INFO   ] [GL          ] OpenGL parsed version: 4, 3
[INFO   ] [GL          ] Shading version <b'4.30 - Build 20.19.15.4549'>
[INFO   ] [GL          ] Texture max size <16384>
[INFO   ] [GL          ] Texture max units <32>
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
[WARNING] [Call to deprecated function __init__ in C]\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\listview.py line 845.Called from C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\lang\builder.py line 582 by _apply_rule().
[WARNING] [Call to deprecated function __init__ in C]\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\adapters\simplelistadapter.py line 49.Called from C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\listview.py line 859 by __init__().
[WARNING] [Call to deprecated function __init__ in C]\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\adapters\adapter.py line 111.Called from C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\adapters\simplelistadapter.py line 55 by __init__().
[WARNING] [Call to deprecated function __init__ in C]\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\abstractview.py line 42.Called from C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\listview.py line 865 by __init__().
[WARNING] [Call to deprecated function __init__ in C]\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\adapters\listadapter.py line 185.Called from C:\Users\amars\PycharmProjects\ReportingApp\reportingapp.kv line 85 by <module>().
[WARNING] [Call to deprecated function __init__ in C]\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\adapters\adapter.py line 111.Called from C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\adapters\listadapter.py line 186 by __init__().
[INFO   ] [Base        ] Start application main loop
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
   File "C:/Users/amars/PycharmProjects/ReportingApp/main.py", line 50, in <module>
     ReportingApp().run()
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\app.py", line 826, in run
     runTouchApp()
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\base.py", line 502, in runTouchApp
     EventLoop.window.mainloop()
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\core\window\window_sdl2.py", line 727, in mainloop
     self._mainloop()
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\core\window\window_sdl2.py", line 460, in _mainloop
     EventLoop.idle()
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\base.py", line 340, in idle
     self.dispatch_input()
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\base.py", line 325, in dispatch_input
     post_dispatch_input(*pop(0))
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\base.py", line 231, in post_dispatch_input
     listener.dispatch('on_motion', etype, me)
   File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\core\window\__init__.py", line 1360, in on_motion
     self.dispatch('on_touch_down', me)
   File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\core\window\__init__.py", line 1376, in on_touch_down
     if w.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\screenmanager.py", line 1191, in on_touch_down
     return super(ScreenManager, self).on_touch_down(touch)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\widget.py", line 460, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\relativelayout.py", line 288, in on_touch_down
     ret = super(RelativeLayout, self).on_touch_down(touch)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\widget.py", line 460, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\widget.py", line 460, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\widget.py", line 460, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\behaviors\button.py", line 151, in on_touch_down
     self.dispatch('on_press')
   File "kivy\_event.pyx", line 703, in kivy._event.EventDispatcher.dispatch
   File "kivy\_event.pyx", line 1214, in kivy._event.EventObservers.dispatch
   File "kivy\_event.pyx", line 1098, in kivy._event.EventObservers._dispatch
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\lang\builder.py", line 64, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File "C:\Users\amars\PycharmProjects\ReportingApp\reportingapp.kv", line 61, in <module>
     root.add_project_screen(project_name, project_address, project_scope)
   File "C:/Users/amars/PycharmProjects/ReportingApp/main.py", line 38, in add_project_screen
     s = Factory.ProjectScreen(name=name)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\relativelayout.py", line 265, in __init__
     super(RelativeLayout, self).__init__(**kw)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\floatlayout.py", line 65, in __init__
     super(FloatLayout, self).__init__(**kwargs)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\layout.py", line 76, in __init__
     super(Layout, self).__init__(**kwargs)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\widget.py", line 348, in __init__
     Builder.apply(self, ignored_consts=self._kwargs_applied_init)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\lang\builder.py", line 469, in apply
     self._apply_rule(widget, rule, rule, ignored_consts=ignored_consts)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\lang\builder.py", line 544, in _apply_rule
     cls = Factory_get(cname)
   File "C:\Users\amars\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\factory.py", line 130, in __getattr__
     raise AttributeError
 AttributeError

Process finished with exit code 1

如您所见,每当我单击应用程序中的按钮以调用 add_project_screen 方法时,应用程序将停止在 python 文件的第 38 行。 add_project_list_item 方法似乎适用于列表视图。

谢谢。

问题 - 属性错误

AttributeError 的原因是拼写错误,class 规则中的 button:<ProjectScreen>:

解决方案

button:替换为Button:

建议

kv 文件

  1. 将动态 class、<ProjectScreen@Screen>: 替换为 class 规则、<ProjectScreen>:
  2. BoxLayout: 小部件实例化为 class 规则的子项,<ProjectScreen>: 因为有标签和按钮小部件,并防止小部件重叠。

片段

<ProjectScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'hook up project info here'
        Button:
            text: 'Back'

Python代码

  1. 删除导入语句,from kivy.factory import Factory
  2. add_project_screen() 方法中删除 s = Factory.ProjectScreen(name=name)
  3. self.manager.add_widget(s)替换为self.manager.add_widget(ProjectScreen(name=name))

片段

def add_project_screen(self,project_name_text_input, project_address_text_input, project_scope_text_input):
    name = project_name_text_input.text
    self.manager.add_widget(ProjectScreen(name=name))

例子

main.py

from kivy.app import App    
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.listview import ListItemButton


class ProjectListButton(ListItemButton):
    pass


class HomeScreen(Screen):
    pass


class ProjectListScreen(Screen):
    project_list = ObjectProperty(None)


class ProjectScreen(Screen):
    pass


class NewProjectScreen(Screen):
    project_name_text_input = ObjectProperty(None)
    project_address_text_input = ObjectProperty(None)
    project_scope_text_input = ObjectProperty(None)

    def add_project_list_item(self, project_name_text_input):
        new_project_name = project_name_text_input.text
        project_list_screen = self.manager.get_screen('project_list_screen')
        project_list_screen.project_list.adapter.data.extend([new_project_name])
        # Reset the ListView
        project_list_screen.project_list._trigger_reset_populate()
        #project_name_text_input.text = ''

    def add_project_screen(self,project_name_text_input, project_address_text_input, project_scope_text_input):
        name = project_name_text_input.text
        self.manager.add_widget(ProjectScreen(name=name))


class ReportingApp(App):
    def build(self):
        screen_manager = ScreenManager()
        screen_manager.add_widget(HomeScreen(name="home_screen"))
        screen_manager.add_widget(ProjectListScreen(name="project_list_screen"))
        screen_manager.add_widget(NewProjectScreen(name="new_project_screen"))
        return screen_manager


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

reporting.kv

#:kivy 1.11.0

#:import main main
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
#:import ListItemButton kivy.uix.listview.ListItemButton


<HomeScreen>:
    BoxLayout:
        orientation: "vertical"
        padding: 100
        spacing: 25
        Label:
            size_hint_y: None
            height: 150
            text: "Site Visit Reporting App v1.1"

        Button:
            text: "New Project"
            on_press:
                root.manager.transition.direction = 'left'
                root.manager.current = 'new_project_screen'

        Button:
            text: "Project List"
            on_press:
                root.manager.transition.direction = 'left'
                root.manager.current = 'project_list_screen'

<NewProjectScreen>:

    project_name_text_input: project_name
    project_address_text_input: project_address
    project_scope_text_input: project_scope

    BoxLayout:
        orientation: "vertical"

        Label:
            text: 'Project Name:'
        TextInput:
            id: project_name

        Label:
            text: 'Project Address:'
        TextInput:
            id: project_address

        Label:
            text: 'Project Scope:'
        TextInput:
            id: project_scope

        BoxLayout:
            padding: 15
            spacing: 25
            Button:
                text: 'OK'
                on_press:
                    root.add_project_list_item(project_name)
                    root.add_project_screen(project_name, project_address, project_scope)
            Button:
                text: 'Back'
                on_press:
                    root.manager.transition.direction = 'right'
                    root.manager.current = 'home_screen'

<ProjectListScreen>:

    project_list: project_list_view

    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Current list of projects'
        Button:
            text: 'Back'
            on_press:
                root.manager.transition.direction = 'right'
                root.manager.current = 'home_screen'

        ListView:
            id: project_list_view
            adapter:
                ListAdapter(data=[], cls=main.ProjectListButton)

<ProjectScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'hook up project info here'
        Button:
            text: 'Back'

输出