ali srn
ali srn

Reputation: 573

How to change labels and radiobuttons after a button clicked?

I have programmed a script which takes random four elements from a table and question to the user using tkinter, random and sqlite3. Currently, I can ask a question. Implement four choices with radiobuttons. I can also test if the answer is correct or not and show the result to the user via toplevel().

Problem is, how can I refresh the question after the continue button clicked?

My whole code is below. I have tried refreshing the random numbers and labels under continue_asking or another def called from continue_asking. But it doesn't work at all.

from tkinter import *
from sqlite3 import *
from random import *


class Question(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.grid()
        self.prepare_question()

    def prepare_question(self):
        self.tumu = {0:['ask1','answer1'], # instead of SQL query
                     1:['ask2','answer2'],
                     2:['ask3','answer3'], 
                     3:['ask4','answer4']}
        self.create_widgets()

    def create_widgets(self):
        self.choiceFrame = Frame(self)
        self.choiceFrame.grid(row=2, column=0)
        self.choiceNum = IntVar()
        for i in range(4):
            Radiobutton(self.choiceFrame, text=self.tumu[i][1], variable=self.choiceNum, value=i) \
                        .grid(row=2, column=i, padx=5, pady=5)

        self.q_num = randrange(4)
        self.q_word = self.tumu[self.q_num][0]

        lbl_question = Label(self, text="Which one is the meaning of the word: " + self.q_word, font="Courier 12")
        lbl_question.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky=W)

        txt_question = Text(self, height=1, font="Courier 12", pady=2)
        txt_question.tag_configure("myStyle", font="Courier 12 bold")
        txt_question.insert("end", "Please choose the answer and ")
        txt_question.insert("end", "click okay to see the results.", "myStyle")
        txt_question.configure(state="disabled")
        txt_question.grid(row=1, column=0, columnspan=4, padx=5, sticky=W)

        btn_okay = Button(self, text="Okay", font="12", command=self.a_control)
        btn_okay.grid(row=3, column=0, columnspan=2)

    def a_control(self):
        self.choosenWord = self.q_num
        self.frm_answer = Toplevel()
        self.frm_answer.title("Result")
        self.selectedWord = self.choiceNum.get()
        txt_result = Text(self.frm_answer, height=4, width = 40)

        if self.choosenWord == self.selectedWord:
            txt_result.insert("end", "Congrats! Your answer is correct.\n")
        else:
            txt_result.insert("end","Your answer is not correct.\n")
            txt_result.insert("end", "Correct answer is " + self.tumu[self.q_num][1] + '\n')

        txt_result.insert("end", "Please click Continue to continue.\n")
        txt_result.insert("end", "Click cancel to quit.")
        txt_result.grid(row=0, column=0, columnspan=2, padx = 5, pady=5)
        txt_result.configure(state="disabled")
        btn_continue = Button(self.frm_answer, text="Continue", command=lambda: self.continue_asking(self.frm_answer))
        btn_continue.grid(row=1, column=0, padx=5, pady=5, sticky = W)

        btn_quit = Button(self.frm_answer, text="Cancel", command=self.end_asking)
        btn_quit.grid(row=1, column=1, padx=5, pady=5, sticky = W)

    def continue_asking(self,frm_answer):
        frm_answer.destroy()

    def end_asking(self):
        root.destroy()

root = Tk()
app = Question(root)
root.mainloop()

I have tried adding prepare_question to continue_asking. It keeps asking questions but widgets are not changing. They are just overlapping.enter image description here

Upvotes: 1

Views: 151

Answers (1)

PRMoureu
PRMoureu

Reputation: 13327

EDIT

So let's restart from scratch, i was totally wrong because no widget was removed and they stacked in the main Frame children list.

You still don't need to write so much code, mostly move some parts.

First, to be able to update the widgets and prepare the new question peacefully, move self.create_widgets() in the constructor and put the random index self.q_num and self.q_word inside prepare_question, since it belongs to the logic of the question creation.

In create_widgets() you only need to keep some control on the label question, so we add self.lbl_question...

Finally, i suggest to create a new function update_widgets(), but you can put the logic inside continue_asking().

In this function, call prepare_question to update the next question (sql query and random stuff). Since we move the random index, everything is ready to update each widget:

  • text of the question label
  • text of radiobuttons. I'm not so proud of the loop to change those, but that'll do the trick. (we keep the values created for the indexes to match the new ones, i'm not very sure about this logic with SQL queries, i follow your first implementation with text=self.tumu[i][1])

If someone can tell how to get the radiobutton value more easily, i'm interested

Here is the whole code:

from tkinter import *
from sqlite3 import *
from random import *


class Question(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.grid()
        self.prepare_question()
        self.create_widgets()

    def prepare_question(self):
        self.tumu = {0:['ask1','answer1'], # instead of SQL query
                     1:['ask2','answer2'],
                     2:['ask3','answer3'], 
                     3:['ask4','answer4']}

        self.q_num = randrange(4)
        self.q_word = self.tumu[self.q_num][0]

    def create_widgets(self):
        self.choiceFrame = Frame(self)
        self.choiceFrame.grid(row=2, column=0)
        self.choiceNum = IntVar()
        for i in range(4):
            Radiobutton(self.choiceFrame, text=self.tumu[i][1], variable=self.choiceNum, value=i) \
                        .grid(row=2, column=i, padx=5, pady=5)

        self.lbl_question = Label(self, text="Which one is the meaning of the word: " + self.q_word, font="Courier 12")
        self.lbl_question.grid(row=0, column=0, columnspan=4, padx=5, pady=5, sticky=W)

        txt_question = Text(self, height=1, font="Courier 12", pady=2)
        txt_question.tag_configure("myStyle", font="Courier 12 bold")
        txt_question.insert("end", "Please choose the answer and ")
        txt_question.insert("end", "click okay to see the results.", "myStyle")
        txt_question.configure(state="disabled")
        txt_question.grid(row=1, column=0, columnspan=4, padx=5, sticky=W)

        btn_okay = Button(self, text="Okay", font="12", command=self.a_control)
        btn_okay.grid(row=3, column=0, columnspan=2)

    def a_control(self):
        self.choosenWord = self.q_num
        self.frm_answer = Toplevel()
        self.frm_answer.title("Result")
        self.selectedWord = self.choiceNum.get()
        txt_result = Text(self.frm_answer, height=4, width = 40)

        if self.choosenWord == self.selectedWord:
            txt_result.insert("end", "Congrats! Your answer is correct.\n")
        else:
            txt_result.insert("end","Your answer is not correct.\n")
            txt_result.insert("end", "Correct answer is " + self.tumu[self.q_num][1] + '\n')

        txt_result.insert("end", "Please click Continue to continue.\n")
        txt_result.insert("end", "Click cancel to quit.")
        txt_result.grid(row=0, column=0, columnspan=2, padx = 5, pady=5)
        txt_result.configure(state="disabled")
        btn_continue = Button(self.frm_answer, text="Continue", command=self.continue_asking)
        btn_continue.grid(row=1, column=0, padx=5, pady=5, sticky = W)

        btn_quit = Button(self.frm_answer, text="Cancel", command=self.end_asking)
        btn_quit.grid(row=1, column=1, padx=5, pady=5, sticky = W)

    def continue_asking(self):
        self.frm_answer.destroy()
        self.update_widgets()

    def update_widgets(self):
        self.prepare_question()

        # change question text 
        self.lbl_question.configure(text = "Which one is the meaning of the word: " + self.q_word)

        # change Radiobutton
        for child in self.choiceFrame.children.values():
            index = child.config()['value'][4]                              
            child.configure(text = self.tumu[index][1])
            if index == 0: # reset the focus
                child.select()

    def end_asking(self):
        root.destroy()

root = Tk()
app = Question(root)
root.mainloop()


First crap post: (the not to do part)

You don't need to change so much code to fix the present issue, have you already tried the following ?

def continue_asking(self,frm_answer):
    frm_answer.destroy()
    self.prepare_question()

I won't review the whole code, there is another place for that, but you can also avoid the lambda when you call continue_asking(), since you store the frame in self.frm_answer

    btn_continue = Button(self.frm_answer, text="Continue", command=self.continue_asking)
    # [...]

def continue_asking(self):
    self.frm_answer.destroy()
    self.prepare_question()

Upvotes: 1

Related Questions