Nourless
Nourless

Reputation: 854

Why does kivy root.manager return None?

I am currently building an app for self measurement. I've faced the following problem : I want to have a result screen, with progress bars for my achievements. So far it looks like this:

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

The problem is, root.manager works fine when applied to the Button and returns to the given screen. However in ProgressBar it appears to be None and returns the following error:

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

What is wrong?

The full code:

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()



        

Upvotes: 0

Views: 230

Answers (1)

ApuCoder
ApuCoder

Reputation: 2908

First of all value : root.manager.get_screen('music').slider.value is wrong because root.manager.get_screen('music') is supposed to be a Screen object and by default it doesn't have (also not in your code) any prop. slide.

Perhaps you mean value : root.manager.get_screen('music').ids.slider.value. But this will not solve your problem. Let's try to analyze the problem in simple way.

When the method run is called, the kv-rules are applied (by method load_kv) first then method build is called. In the method build you defined ScreenManager first thereafter added the Screens and described the screens in kvlang. Now when it encounters your .kv file and looks for root.manager (in value : root.manager.get_screen('music').ids.slider.value) it finds None (the default value, as it has not yet been added to ScreenManager, defined in your build method). That's the reason behind the AttributeError.

Now to solve this issue one of many ways could be,

  1. You delay the value assigning process by a (built-in or custom) method like on_enter etc. as,
<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. Set a conditional statement (likely to be preferable) as,
        ProgressBar:
            id: pb
            max : 1000
            value : root.manager.get_screen('music').ids.slider.value if root.manager is not None else 0
  1. Define the root (i.e. ScreenManager here) entirely in kvlang.

Other ways could be creating a property for slide.value in the associate class; manage it in the build method etc.

Upvotes: 0

Related Questions