为什么kivy root.manager return None?

Why does kivy root.manager return None?

我目前正在构建一个用于自我测量的应用程序。我遇到了以下问题:我想要一个结果屏幕,其中包含我的成就进度条。到目前为止它看起来像这样:

<ResultScreen>:
    BoxLayout:
        Button:
            text: 'Назад, к музыке'
            on_press: root.manager.current = 'music'
        ProgressBar:
            max : 1000
            value : root.manager.get_screen('music').slider.value

问题是,root.manager 应用于给定屏幕的 Button 和 returns 时工作正常。但是在 ProgressBar 中似乎是 None 和 returns 出现以下错误:

AttributeError: 'NoneType' object has no attribute 'get_screen'

怎么了?

完整代码:

main.py:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import (
    NumericProperty, ReferenceListProperty, ObjectProperty
)
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.uix.screenmanager import ScreenManager, Screen

class PongScreen(Screen):
    pass
class MusicScreen(Screen):
    pass
class DiscreteScreen(Screen):
    pass
class ResultScreen(Screen):
    pass
class PongApp(App):
    def build(self):
        sm = ScreenManager()
        sm.add_widget(PongScreen(name='menu'))
        sm.add_widget(MusicScreen(name='music'))   
        sm.add_widget(DiscreteScreen(name='discrete')) 
        sm.add_widget(ResultScreen(name='result')) 
     
        return sm



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

pong.kv:

#:kivy 1.0.9
#:import Factory kivy.factory.Factory

<MusicScreen>:
    BoxLayout:
        Button:
            text: 'Назад в меню'
            on_press: root.manager.current = 'menu'
        Button:
            text: 'Дальше, к дискретке'
            on_press: root.manager.current = 'discrete'
        Slider:
            id: slider
            min: 0
            max: 20
            step: 1
            orientation: 'vertical'
        Label:
            text: 'Тактов выучено\n' + str(slider.value)
            
        CheckBox:
            id : new_song_checkbox
        Label:
            text : 'Новую песню выучил?\n' + str(new_song_checkbox.active)
        CheckBox:
            id : music_pleasure_checkbox
        Label:
            text : 'Доволен ли исполнением\n' + str(music_pleasure_checkbox.active)
            
<DiscreteScreen>:
    BoxLayout:
        Button:
            text: 'В меню'
            on_press: root.manager.current = 'menu'
        Button:
            text: 'Назад, к музыке'
            on_press: root.manager.current = 'music'
        Slider:
            id: slider
            min: 0
            max: 180
            step: 15
            orientation: 'vertical'
        Label:
            text: 'Минут на код затрачено\n' + str(slider.value)
            
        CheckBox:
            id : article_checkbox
        Label:
            text : 'Статью прочитал?\n' + str(article_checkbox.active)
        CheckBox:
            id : lecture_checkbox
        Label:
            text : 'Посмотрел ли лекцию?\n' + str(lecture_checkbox.active)
            
<DiscreteScreen>:
    BoxLayout:
        Button:
            text: 'В меню'
            on_press: root.manager.current = 'menu'
        Button:
            text: 'Назад, к музыке'
            on_press: root.manager.current = 'music'
        Button:
            text: 'Дальше, к результатам'
            on_press: root.manager.current = 'result'
        Slider:
            id: slider
            min: 0
            max: 180
            step: 15
            orientation: 'vertical'
        Label:
            text: 'Минут на код затрачено\n' + str(slider.value)
            
        CheckBox:
            id : article_checkbox
        Label:
            text : 'Статью прочитал?\n' + str(article_checkbox.active)
        CheckBox:
            id : lecture_checkbox
        Label:
            text : 'Посмотрел ли лекцию?\n' + str(lecture_checkbox.active)
            
<ResultScreen>:
    BoxLayout:
        Button:
            text: 'Назад, к музыке'
            on_press: root.manager.current = 'music'
        ProgressBar:
            max : 1000
            value : root.manager.get_screen('music').slider.value
<PongScreen>:
    BoxLayout:
        Button:
            text: 'К музыке'
            on_press: root.manager.current = 'music'
        Button:
            text: 'К дискретке'
            on_press: root.manager.current = 'discrete'
        Button:
            text: 'К результатам'
            on_press: root.manager.current = 'result'
        Button:
            text: 'Выход'
            on_press: app.stop()



        

首先,value : root.manager.get_screen('music').slider.value 是错误的,因为 root.manager.get_screen('music') 应该是一个 Screen 对象,默认情况下它没有(也不在您的代码中)任何道具。 slide.

也许你的意思是 value : root.manager.get_screen('music').ids.slider.value。但这不会解决你的问题。让我们尝试用简单的方式来分析问题。

调用方法 run 时,首先应用 kv-rules(通过方法 load_kv),然后调用方法 build。在方法 build 中,您首先定义了 ScreenManager,然后添加了 Screens 并在 kvlang 中描述了屏幕。现在,当它遇到您的 .kv 文件并查找 root.manager(在 value : root.manager.get_screen('music').ids.slider.value 中)时,它会找到 None(默认值,因为它尚未添加到 ScreenManager,在您的 build 方法中定义)。这就是 AttributeError.

背后的原因

现在解决这个问题的方法有很多,

  1. 您通过 on_enter 等(built-in 或自定义)方法延迟了值分配过程,因为,
<ResultScreen>:
    on_enter: pb.value = self.manager.get_screen('music').ids.slider.value
    BoxLayout:
        Button:
            text: 'Назад, к музыке'
            on_press: root.manager.current = 'music'
        ProgressBar:
            id: pb
            max : 1000
  1. 将条件语句(可能更可取)设置为,
        ProgressBar:
            id: pb
            max : 1000
            value : root.manager.get_screen('music').ids.slider.value if root.manager is not None else 0
  1. 完全在kvlang.
  2. 中定义root(即这里的ScreenManager

其他方法可能是在关联 class 中为 slide.value 创建一个 属性;在 build 方法等中管理它