Search code examples
pythonkivykivy-language

Trouble using toggle button for quiz in kivy


In my quiz app, the toggle buttons are used as options to get the answer from the user. But whenever a toggle button is clicked, it is in a 'down' state for the next question, but whenever I unclick that button, the next question appears, and I am missing to answer that question. I want that whenever a new question appears the state of all toggle buttons should be 'normal'. here is the code for main file:

import json
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App

a=0
score=0
sca=0
class MyLayout(BoxLayout):
    global data
    f=open('questions.json')
    data=json.load(f)
  
    def openquestion(self):
        global a
        try:
            self.ids.ques.text=data['category1']['question'][a]   
            self.ids.opt1.text=data['category1']['options'][a][0]
            self.ids.opt2.text=data['category1']['options'][a][1]
            self.ids.opt3.text=data['category1']['options'][a][2]
            self.ids.opt4.text=data['category1']['options'][a][3]
            a+=1
        except:
            print('all questions completed')

    
    def checkscore(self,bu_id):
        global score
        global sca
        answer=data['category1']['answer'][sca]
        if(bu_id==answer):
            score+=1
        print('score: ',score,'question: ',sca+1,'button pressed: ',bu_id,'answer no:',answer)
        sca+=1

class rough(App):
    def build(self):
        return MyLayout()

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

here is the .kv file:

#:kivy 2.1.0
<QLabel@Label>:
    halign:'center'
    text_size:self.width,None
<ToggleButton>:
    size:0.6,0.4
<MyLayout>:
    orientation:'vertical'
    size:root.width,root.height
    padding:20
    spacing:10
    BoxLayout:
        orientation:'vertical'
        size:root.width,root.height
        padding:20
        spacing:10
        BoxLayout:
            id:options
            orientation:'vertical'
            spacing:7
            QLabel:
                id:ques
                text:'Questions coming soon...'
            ToggleButton:
                group:options
                id:opt1
                text:'option01'
                on_press:
                    root.checkscore(1)
                    root.openquestion()

            ToggleButton:
                group:options
                id:opt2
                text:'option02'
                on_press:
                    root.checkscore(2)
                    root.openquestion()

            ToggleButton:
                group:options
                id:opt3
                text:'option03'
                on_press:
                    root.checkscore(3)
                    root.openquestion()

            ToggleButton:
                group:options
                id:opt4
                text:'option04'
                on_press:
                    root.openquestion()
                    root.checkscore(4)

            GridLayout:
                cols:2
                spacing:30
                Button:
                    text:'Next'
                    on_release:
                        root.openquestion()
                Button:
                    text:'Exit'
                    on_release:
                        exit()

I don't think there is any need for JSON file, please just assume that there are some questions and answers for the same.


Solution

  • If you want the state of all the ToggleButtons to be normal when a question is opened, then add code to set their states to normal in your openquestion() method. Also, note that on_press is triggered whenever you click on a ToggleButton. If you want the action to depend on whether the state is down or normal, then you need to put that logic in the code that gets triggered. So, in your openquestion() method you can add a line for each ToggleButton like:

    self.ids.opt1.state = 'normal'
    

    Then remove the root.openquestion() from each on_press: of the ToggleButtons.

    The logic for opening a new question can then be added to the checkscore() method. Something like:

    def checkscore(self, bu_id):
        global score
        global sca
        answer = data['category1']['answer'][sca]
        if (bu_id == answer):
            score += 1
        print('score: ', score, 'question: ', sca + 1, 'button pressed: ', bu_id, 'answer no:', answer)
        sca += 1
        Clock.schedule_once(self.openquestion, 1)
    

    I added the delay, using Clock.schedule_once(), so that the user will notice the change. You could also add a Popup or something to notify the user of whether the question was answered correctly or not. Using the Clock.schedule_once() requires a small change to the openquestion() signature:

    def openquestion(self, *args):