Reputation: 573
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.
Upvotes: 1
Views: 151
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=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