Managing the value of a variable among several functions in Kivy project

0

Be the following layout of my app: I am looking for when the "Successful question" button is clicked, the variable score = 10 and when the "Failed question" button is clicked, the variable score = -5 For both cases, the purple label also shows the value of the variable score. I currently have the following error:

  

File "design.kv", line 54, in on_release: root.showscore (score) NameError: name 'score' is not defined

Below I give a possible solution although it does not work for me.

My code in python:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder

Builder.load_file('design.kv')

class MyWidget(BoxLayout):


    def __init__(self):
        super(MyWidget, self).__init__()

    def showquestion(self):
        with open("question.txt","r") as f:
            filetext = f.read()
            self.ids['label1'].text = filetext

    def showanswer(self):
        with open("answer.txt","r") as f:
            filetext = f.read()
            self.ids['label2'].text = filetext

    def goodanswer(self):
        score = 10
        return score 

    def badanswer(self):
        score = -5
        return score

    def showscore(self,score):
        self.ids['label3'].text = str(score)

class myApp(App):
    def build(self):
        return MyWidget()
    def on_pause(self): 
        return True
    def on_resume(self): 
        pass


if __name__ in ('__main__', '__android__'): 
    myApp().run()

My code in .kv:

<MyWidget>:
    size: root.size
    orientation: 'vertical'
    padding: 20
    spacing: 0

    #label1 muestra la pregunta
    Label:
        id: label1
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba: 191/255.0, 144/255.0, 63/255.0, 1
            Rectangle:
                size: self.size
                pos: self.pos

    #label2 muestra la respuesta
    Label:
        id: label2
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba:  110/255.0, 191/255.0, 63/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size

    Label:
        id: label3
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba: 74/255.0, 25/255.0, 44/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size


    BoxLayout:
        padding: 0,10,0,0
        spacing: 10
        size_hint:1, 0.25
        orientation: 'horizontal'
        Button:
            id: button1
            text: "Mostrar respuesta"
            on_release: root.showanswer()
        Button:
            id: button2
            text: "Pregunta Acertada"
            on_release: root.goodanswer()
            on_release: root.score
            on_release: root.showscore(score)
        Button:
            id: button3
            text: "Pregunta Fallada"
            on_release: root.badanswer()
            on_release: root.score
            on_release: root.showscore(score)
        Button:
            id: button4
            text: "Mostrar pregunta"
            on_release: root.showquestion()

One option I can think of is the following, using the expression #:set name value see doc. :

        Button:
            id: button2
            text: "Pregunta Acertada"
            on_release: 
                #:set score 10
            on_release: root.showscore(score)
        Button:
            id: button3
            text: "Pregunta Fallada"
            on_release:
                #:set score -5
            on_release: root.showscore(score)

The problem is that it seems to be done in a global way, prevailing the last value that has been given, in this case, -5. Maybe explore the way to solve this problem by the .kv file instead of the .py, but I'll leave that for a later question.

    
asked by Mr. Baldan 19.04.2017 в 22:45
source

1 answer

1

The problem is that as the error tells you the variable score has never been defined. You are trying to change the value of a variable that does not exist. You must define it, for example, as an instance attribute in __init__ . Within the methods goodanswer , badanswer and showscore you must refer to it as a class attribute, prefixing self to the name. Similarly in the .kv you must define the class to which the variable belongs, in this case root .

Another important thing is that you should not do this:

on_release: root.goodanswer()
on_release: root.showscore(root.score)

If you do this you will find unexpected answers, in fact if you press the button once, only the first line is executed, the second click does the second, the third clicks the first one again, etc. For each click to execute more than one order you must use a single line:

on_release: root.goodanswer(), root.showscore(root.score)

The code would look like this:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder

Builder.load_file('design.kv')

class MyWidget(BoxLayout):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.score = 0

    def showquestion(self):
        with open("question.txt","r") as f:
            filetext = f.read()
            self.ids['label1'].text = filetext

    def showanswer(self):
        with open("answer.txt","r") as f:
            filetext = f.read()
            self.ids['label2'].text = filetext

    def goodanswer(self):
        self.score = 10

    def badanswer(self):
        self.score = -5

    def showscore(self,score):
        self.ids['label3'].text = str(self.score)

class myApp(App):
    def build(self):
        return MyWidget()
    def on_pause(self):
        return True
    def on_resume(self):
        pass


if __name__ in ('__main__', '__android__'):
    myApp().run()

design.kv:

<MyWidget>:
    size: root.size
    orientation: 'vertical'
    padding: 20
    spacing: 0

    #label1 muestra la pregunta
    Label:
        id: label1
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba: 191/255.0, 144/255.0, 63/255.0, 1
            Rectangle:
                size: self.size
                pos: self.pos

    #label2 muestra la respuesta
    Label:
        id: label2
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba:  110/255.0, 191/255.0, 63/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size

    Label:
        id: label3
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba: 74/255.0, 25/255.0, 44/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size


    BoxLayout:
        padding: 0,10,0,0
        spacing: 10
        size_hint:1, 0.25
        orientation: 'horizontal'
        Button:
            id: button1
            text: "Mostrar respuesta"
            on_release: root.showanswer()
        Button:
            id: button2
            text: "Pregunta Acertada"
            on_release: root.goodanswer(),root.showscore(root.score)

        Button:
            id: button3
            text: "Pregunta Fallada"
            on_release: root.badanswer(), root.showscore(root.score)
        Button:
            id: button4
            text: "Mostrar pregunta"
            on_release: root.showquestion()

The simple way to do this is to use a Property , this type of variable allows automatic updating of the values in the Label:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import NumericProperty

Builder.load_file('design.kv')


class MyWidget(BoxLayout):
    score = NumericProperty()

    def __init__(self):
        super(MyWidget, self).__init__()

    def showquestion(self):
        with open("question.txt", "r") as f:
            filetext = f.read()
            self.ids['label1'].text = filetext

    def showanswer(self):
        with open("answer.txt", "r") as f:
            filetext = f.read()
            self.ids['label2'].text = filetext


class myApp(App):
    def build(self):
        return MyWidget()

    def on_pause(self):
        return True

    def on_resume(self):
        pass


if __name__ in ('__main__', '__android__'):
    myApp().run()

design.kv:

<MyWidget>:
    size: root.size
    orientation: 'vertical'
    padding: 20
    spacing: 0

    #label1 muestra la pregunta
    Label:
        id: label1
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba: 191/255.0, 144/255.0, 63/255.0, 1
            Rectangle:
                size: self.size
                pos: self.pos

    #label2 muestra la respuesta
    Label:
        id: label2
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba:  110/255.0, 191/255.0, 63/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size

    Label:
        id: label3
        color: 1,0,1,1
        text: str(root.score)
        canvas.before:
            Color:
                rgba: 74/255.0, 25/255.0, 44/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size


    BoxLayout:
        padding: 0,10,0,0
        spacing: 10
        size_hint:1, 0.25
        orientation: 'horizontal'
        Button:
            id: button1
            text: "Mostrar respuesta"
            on_release: root.showanswer()
        Button:
            id: button2
            text: "Pregunta Acertada"
            on_release: root.score += 10

        Button:
            id: button3
            text: "Pregunta Fallada"
            on_release: root.score -= 5
        Button:
            id: button4
            text: "Mostrar pregunta"
            on_release: root.showquestion()

In this case I modified the behavior somewhat to show other capabilities. In this case score is 0 when starting. Each time you press Successful Response add 10 points and each time you press Missed Response subtract 5. All this is done from kv without using any method as we did before but note that variable score has to be declared in main in this case as in the previous one.

The grace of the Property is that if we modify its value at any time automatically and from any side all the widgets that use that value are updated, as is the case of our Label without having to do anything else.

I think the best option is the previous one but it can be done in more ways, for example doing everything in the .kv, without declaring even the variable in the main.py:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder

Builder.load_file('design7.kv')


class MyWidget(BoxLayout):


    def __init__(self):
        super(MyWidget, self).__init__()

    def showquestion(self):
        with open("question.txt", "r") as f:
            filetext = f.read()
            self.ids['label1'].text = filetext

    def showanswer(self):
        with open("answer.txt", "r") as f:
            filetext = f.read()
            self.ids['label2'].text = filetext


class myApp(App):
    def build(self):
        return MyWidget()

    def on_pause(self):
        return True

    def on_resume(self):
        pass


if __name__ in ('__main__', '__android__'):
    myApp().run()

design.kv:

<MyWidget>:
    score: 0
    size: root.size
    orientation: 'vertical'
    padding: 20
    spacing: 0

    #label1 muestra la pregunta
    Label:
        id: label1
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba: 191/255.0, 144/255.0, 63/255.0, 1
            Rectangle:
                size: self.size
                pos: self.pos

    #label2 muestra la respuesta
    Label:
        id: label2
        color: 1,0,1,1
        canvas.before:
            Color:
                rgba:  110/255.0, 191/255.0, 63/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size

    Label:
        id: label3
        color: 1,0,1,1
        text: str(root.score)
        canvas.before:
            Color:
                rgba: 74/255.0, 25/255.0, 44/255.0, 1
            Rectangle:
                pos:self.pos
                size: self.size


    BoxLayout:
        padding: 0,10,0,0
        spacing: 10
        size_hint:1, 0.25
        orientation: 'horizontal'
        Button:
            id: button1
            text: "Mostrar respuesta"
            on_release: root.showanswer()
        Button:
            id: button2
            text: "Pregunta Acertada"
            on_release: root.score += 10

        Button:
            id: button3
            text: "Pregunta Fallada"
            on_release: root.score -= 5
        Button:
            id: button4
            text: "Mostrar pregunta"
            on_release: root.showquestion()
    
answered by 19.04.2017 / 23:40
source