Hank
Hank

Reputation: 23

AttributeError: 'NoneType' object has no attribute 'ids' (self.root returns 'None' )

I am trying to access from my .py file a button/label defined in .kv file by its ID and change its 'text' value. So I wrote in my build function:

#self.root.ids.button.text='newValue'

it says AttributeError: 'NoneType' object has no attribute 'ids' I have tried many ways to access 'self.root' from other places in my class ,but none of them worked.

I wonder why I have no self.root in my class?

This is my kv file:

<MyRun>:

    BoxLayout:
        id:bx1
        orientation: 'vertical'
        spacing: 10
        padding: [20]
        canvas:
            Color:
                rgba: .6, .6, .6, .3
            Rectangle:
                size: root.size
                source: 'pic\germanFlag.png'

        ActionBar:
            ActionView:
                id: av
                ActionPrevious:
                    with_previous: False
                    title: 'DeutschLerne' 
        Label:
            id:qtitle
            text: 'Percentage of Question Answered:{}%'.format(int(7))
            font_size: '30sp'
            size_hint_y: None
            height: '48dp'

        Label:
            id:qbody
            text: 'The answer is '+('{}'.format(str("Richtig!")) if 1>0 else '{}'.format(str("Falsch!")))
            color: [.3, 1, .5, 1] if 1>0 else [1, .2, .3, 1]
            font_size: '30sp'
            size_hint_y: None
            height: '48dp'

        Widget:
            canvas:
                Color:
                    rgba: .6 , .6 , .6 , .3
                Rectangle:
                    source: "pic\questionCanvas.png"
                    size: [self.width,self.height*3]
                    pos: [self.x,self.y-300]


        BoxLayout:
            id:bx2
            spacing: 10
            padding: [10, 200 , 10 , 0]

            Button:
                id:selA
                text: 'A'
                size_hint_y: None
                size_hint_x: .5
                on_release: app.answer(self.text)
                on_release: app.disableButton(self)
            Button:
                id:selB
                text: 'B'
                size_hint_y: None
                size_hint_x: .5 
                on_release: app.answer(self.text)
                on_release: app.disableButton(self)
            Button:
                id:selC
                text: 'C'
                size_hint_y: None
                size_hint_x: .5
                on_release: app.answer(self.text)
                on_release: app.disableButton(self)
            Button:
                id:selD
                text: 'D'
                size_hint_y: None
                size_hint_x: .5
                on_release: app.answer(self.text)
                on_release: app.disableButton(self)
                #on_release: app.go_screen(0)

        ProgressBar:
            id: pb
            size_hint_x: 1
            size_hint_y: None
            height: '48dp'
            value: (app.time * 20) % 100.

This is my main class, Desutsch.py

from time import time
from kivy.app import App
from os.path import dirname, join
from kivy.properties import NumericProperty, StringProperty, BooleanProperty,\
    ListProperty,OptionProperty
from kivy.clock import Clock
from kivy.animation import Animation
from kivy.uix.screenmanager import Screen
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from functools import partial
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
import random
NUM_OF_NOUN = 416       #constant . don't change its value
class MyRun(BoxLayout):
    pass
class DeutschApp(App):
    index = NumericProperty(-1)     
    challenges = NumericProperty(0)
    time = NumericProperty(0)
    wl = ListProperty([])
    len = 0                         

    ans = []            #.[0] => answer [1:3]
    ansID=[]            #.[0] => answer [1:3]
    order=[]            #random list(1~4)

    def build(self):
        Builder.load_file('Question.kv')
        self.title = 'Destsch Lernen'
        Clock.schedule_interval(self._update_clock, 1 / 60.)

        self.screens = {}
        self.available_screens = sorted([
            'Menu','Question'])

        len = self.loadWordList('default.txt')
        self.question(len)
        self.root.ids.selA.text='newValue'
        return MyRun()

    def go_screen(self, idx):
        self.index = idx
        self.root.ids.sm.switch_to(load_screen(idx), direction='left')

    def load_screen(self, index):
        if(0==index):
            screen = Builder.load_file('Question.kv')
        if(1==index):
            screen = Builder.load_file('Menu.kv')
        self.screens[index] = screen
        return screen
    #load question from local file

    def loadWordList(self,WordList):
        fd = open (WordList,'r')
        fd.readline()
        for line in fd:
            #line.split("|"):id|german|translation|[sentence1^^sen2^^]|hisCount|dailyCount|percentage
            line = line.rstrip("\n")
            tmp  = line.split('|')
            tmp[3] = tmp[3].replace("[", "")
            tmp[3] = tmp[3].replace("]", "")
            sen = tmp[3].split('^^')
            self.wl.append({'id': int(tmp[0]), 'german': tmp[1],'translation': tmp[2], 'sentences': sen, 'hisCount': int(tmp[4]), 'dailyCount':  int(tmp[5]), 'percentage':int(tmp[-1]) })
            #sentence encode not good
            print tmp[0]
            self.len = int(tmp[0])  #self.len = self.len+1
        print self.len
        self.question(self.len)
        return self.len

    def question(self,len):
        x = random.randint(0,len)
        ans = self.ans
        ansID = self.ansID

    #id0~416 =nouns  #Nouns self.wl[x].get('german')
        if x in range(0,NUM_OF_NOUN+1):                     
            qType = 0   #0=def , 1=senten 2=pic qType= random.randint(0,2)

            if (0==qType):
                ans.append({'id':x , 'sel':self.wl[x].get('translation')})
                ansID.append( int(x) )
                for all in range(0,3):
                    y = random.randint(0,NUM_OF_NOUN+1)
                    while( (y in ansID) or (y not in range(0,NUM_OF_NOUN+1)) ):
                        y = random.randint(0,NUM_OF_NOUN+1)
                    ans.append({'id':y , 'sel':self.wl[y].get('translation')})
                    ansID.append( int(y) )
                print ans               
            #elif:
            #else:

            #assign selections to buttons randomly
            order = self.RandomSelList
            print order



    def RandomSelList(self):
        a = random.sample(range(4), 4)
        return a

    def checkAnswer(self,ansList,choice):
        right = 0
        print "++"
        print ansList
        print choice
        return right

    def answer(self,choice):
        #print self.order[0]
        self.checkAnswer(self.ans,choice)
        print 'answer(%s)'%choice

    def Test(len):
        print random.randint(0,len)

    def _update_clock(self, dt):
        self.time = time()
#modify screen
    def selChange(self,button,sel):
        print 'selChange()'
        print self
        print self.root
        print super
        #self.root.ids.button.text='sel'

    def disableButton(self,button):
        button.disabled = True

    print super
    #Clock.schedule_interval(Test(len), 1)
if __name__=='__main__':
    DeutschApp().run()

I'm new here . Sorry for the ugly codes. I am still learning how to ask a good question.

Upvotes: 2

Views: 3103

Answers (1)

inclement
inclement

Reputation: 29488

The root attribute is set to whatever you return from the build method, so it doesn't exist when you try to access it (or specifically, has the default value of None instead).

You don't need it though, since you have a reference to the widget anyway. You can change the end of the build method to

root = MyRun()
root.ids.selA.text='newValue'
return root

Upvotes: 3

Related Questions