Using guizero and a text datafile to create GUI - python

So I'm trying to help a student figure out how to create a simple quiz by reading from a text file with the questions, options, and answers. He wants to use guizero instead of the simple built-in input() and print() functions.
I would like him to avoid creating a separate check function for each question, but I don't have much experience with guizero. I've been reading the manual pages, and the below code approximates what we are trying to accomplish but doesn't work because selected_value is not defined until after the program runs. Am I approaching this the wrong way?
from guizero import App, Text, ButtonGroup
app = App(title="Quiz Test", height=800, width=600)
def check_answer(selected_value, answer):
if selected_value == answer:
result.value = "Correct"
else:
result.value = "Wrong"
question_data = []
data_file = open("quiz_ques.txt", 'r')
for line in data_file.read().splitlines():
question_data = line.split(", ")
question_data.reverse() ; question = question_data.pop()
question_data.reverse() ; answer = question_data.pop()
q_options = question_data
text = Text(app, text=question)
choice = ButtonGroup(app, options=q_options, selected=1, command=check_answer, args=[selected_value, answer])
result = Text(app)
data_file.close()
app.display()

try changing
command=check_answer()
to
command=check_answer
in the original version you were calling check_answer() as soon as you defined your program... in the second version it will not be called until the button is clicked

I figured this out using a couple of lists for anyone looking for a solution. Someone who is more of a python expert could probably simplify this, using some pythonic idiom, built-in functions, or standard modules, but this solution works even if it's a bit of a hack. Improvements on this are welcomed. :)
from guizero import App, Text, ButtonGroup
app = App(title="Quiz Test", height=800, width=600)
def check_answer(answer, result, cnt):
if choices[cnt].value == answer:
result.text_color = 'green'
result.value = "Correct"
update_score()
else:
result.text_color = 'red'
result.value = "Wrong"
update_score()
def update_score():
score = 0
for result in results:
if result.value == "Correct":
score += 1
score_txt.value = "Score: " + str(score)
question_data = []
data_file = open("quiz_ques.txt", 'r')
cnt = 0
results = []
choices = []
for line in data_file.read().splitlines():
question_data = line.split(", ")
question_data.reverse() ; question = question_data.pop()
question_data.reverse() ; answer = question_data.pop()
q_options = question_data
question = Text(app, text=question)
question.text_color = 'white'
results.append(Text(app))
choices.append(ButtonGroup(app, options=q_options, command=check_answer, args=[answer, results[cnt], cnt]))
cnt += 1
score_txt = Text(app, color='white', size=40)
data_file.close()
app.display()

Related

How to reload part of the code in tkinter many times?

In the code below, a part of the main code is placed. I want this file, which is updated at different times, to be updated in tekinter, but the problem is that my UI is not updated until it is closed and opened.
style = ttk.Style(GLabel_820)
style.theme_use('clam')
style.configure("mystyle.Treeview", highlightthickness=0,bd=0,font=('Far_Nazanin', 11))
style.configure("mystyle.Treeview.Heading", font=('Far_Nazanin', 15))
style.layout("mystyle.Treeview", [('mystyle.Treeview.treearea', {'sticky': 'nswe'})])
tree = ttk.Treeview(GLabel_820)
tedadtardod=0
filename = 'output/pelak1.xlsx'
filename = r"{}".format(filename)
df = pd.read_excel(filename,engine='openpyxl')
tedadkhat=len(df)
tree["column"] = list(df.columns)
tree["show"] = "headings"
for col in tree["column"]:
tree.heading(col, text=col)
df_rows = df.to_numpy().tolist()
for row in df_rows:
tedadtardod=tedadtardod+1
if tedadtardod>tedadkhat-10:
tree.insert("",0,values=row)
Have you tried placing that part of the code in a loop.
You can use a while loop or a for loop.
Well, with my research I managed to solve my problem, so I'm posting the result here for others to use
def reload(self):
while True :
try :
gf = pd.read_excel('output/pelak1.xlsx',engine='openpyxl')
break
except EOFError as e:
time.sleep(1)
NewPelakLength=len(gf)
global LastPelakLength
global toc
tic = time.perf_counter()
elap=tic-toc
if NewPelakLength != LastPelakLength:# or elap>10
toc = time.perf_counter()
LastPelakLength=len(gf)
self.pelak_list()
self.after(6000, self.reload)

Tkinter: multiple choice quiz app, can't change question (and other issues)

I'm trying to create a multiple choice question app with tkinter but I'm having three problems that I can't solve by myself:
The question don't change, it stuck on the first question.
I have a file with all my questions (more then 50) what I'd like to do is select randomly only 10 of them from the list (five from easy_question list and 5 from the hard_question list).
Is there a way to save in an exel file the ten code of the question that where selected and to know what that person answered (wrong or right doesn't metter)? something like this:
1 2 3 [...]
question 1e 3h 2e
answer 2 3 4
This is a simple version of my file with all my questions, options and correct answers:
easy_questions2=[
"1e. Name?",
"2e. Last name?",
"3e. Birthdate?",
"4e. Food?"
]
easy_options=[
['Marck', 'Mary','Joseph','John'],
['Smith', 'Hartnett','Pitt','Pacino'],
['June', 'October','November','April'],
['All', 'Fries','Pasta','Chicken']
]
easy_answers=[
1,
2,
3,
3
]
hard_questions2=[
"1h. Number?",
"2h. Word?",
"3h. Hour?",
"4h. Color?"
]
hard_options=[
['10', '11','21','55'],
['Book', 'Table','en','Pacino'],
['11', '21','24','18'],
['Yellow', 'Blue','Red','Green']
]
hard_answers=[
3,
4,
1,
2
]
This is my code:
import tkinter as tk
import tkinter.ttk as ttk
from questionslist import easy_questions2, easy_answers, easy_options
from openpyxl import load_workbook
from tkinter import messagebox as mb
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.q_no=0
self.display_title()
self.display_question()
self.opt_selected=tk.IntVar()
self.opts=self.radio_buttons()
self.display_options()
self.buttons()
self.data_size=len(question)
self.correct=0
def display_result(self):
wrong_count = self.data_size - self.correct
correct = f"Correct: {self.correct}"
wrong = f"Wrong: {wrong_count}"
score = int(self.correct / self.data_size * 100)
result = f"Score: {score}%"
mb.showinfo("Result", f"{result}\n{correct}\n{wrong}")
def check_ans(self, q_no):
if self.opt_selected.get() == answer[q_no]:
return True
def next_button(self):
if self.check_ans(self.q_no):
self.correct += 1
self.q_no += 1
if self.q_no==self.data_size:
self.display_result()
self.submit()
self.destroy()
else:
self.display_question()
self.display_options()
def buttons(self):
next_button = tk.Button(self, text="Next",command=self.next_button, width=10)
next_button.pack(pady=50, side="bottom")
def display_options(self):
val=0
self.opt_selected.set(0)
for option in options[self.q_no]:
self.opts[val]['text']=option
val+=1
def display_question(self):
q_no = tk.Label(self, text=question[self.q_no], width=60)
q_no.pack(padx=19, pady=31, anchor="w")
def radio_buttons(self):
q_list = []
while len(q_list) < 4:
radio_button = ttk.Radiobutton(self,text=" ",variable=self.opt_selected,
value = len(q_list)+1)
q_list.append(radio_button)
radio_button.pack(padx=19, anchor="w")
return q_list
def submit(self):
wb = load_workbook('D:\\Python\\quiz\\template.xlsx')
sheet = wb.active
sheet.cell(row=1, column=2).value = "first_name"
sheet.cell(row=1, column=3).value = "correct"
sheet.cell(row=1, column=4).value = "wrong"
sheet.cell(row=2, column=3).value = self.correct
sheet.cell(row=2, column=4).value = self.data_size - self.correct
excel_filename = "D:\\Python\\quiz\\" + "data" + ".xlsx"
wb.save(excel_filename)
question = easy_questions2
options = easy_options
answer = easy_answers
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.title("Quiz")
root.geometry('800x450')
root.mainloop()
If you want to read the questions from a file (or more), you could use (suppose the file is a .txt and you've wrote a question per line):
question_list = []
with open('filename.txt', 'r+') as question_file:
lines = question_file.readlines()
# Let's move the pointer of the file back to the start to resave the questions
# as we don't want to lose them.
question_file.seek(0)
question_file.truncate()
for line in lines:
question_list.append(line) # Put each question at the end of the list.
question_file.write(line)
Use this method to retrieve all the questions and answers from all your files and to put them into lists.
Now you should have a list for the easy questions and one for the hard questions, so, to pick a random question from one list you can use a specific function from the random module, randint(a, b) which is a function that return a value in between 'a' and 'b' ('a' and 'b' included). Try it this way:
from random import randint
# You have a list of 25 hard question, let's call it hard_list
# and a list of 25 easy question, let's call it easy_list
# Now let's use our randint function to retrieve 10 random value between 0 and 24.
# As you haven't done any statement about how to choose the order of the questions,
# I'll take the first five from the hards one and the last 5 from the easy,
# but you can do it in any way you want.
# It may be a little tricky to manage the possibility of same random numbers to come out,
# surely there are better ways to do it, but this works fine.
random_questions = [] # Here I write the questions randomly chosen.
random_numbers = [] # I use this to take into account the random values.
i = 0 # Our counter.
while i < 10:
# If 5 questions have already been chosens, we change the list
# and clear the random values counter once.
if i >= 5:
if i == 5:
random_numbers.clear()
val = randint(0, 24)
if val not in random_numbers:
i += 1
random_numbers.append(val)
random_questions.append(easy_list[val])
else:
val = randint(0, 24)
if val not in random_numbers:
i += 1
random_numbers.append(val)
random_questions.append(hard_list[val])
Now, with your code, I see that the 'check_button' function doesn't check if the user have select an option or not, but maybe it's what you wanted, secondly, your button don't change the question because.. you're assigning a new Label to q_no (which, I suppose, is the number of questions answered so far)? You should assign the Label to another variable and update its text value each time the user click the button.
For example:
self.question_label = tk.Label(self, text=question[self.q_no], ....)
self.question_label.pack(...)
And when the user press the button to the next question, you check if the answer is correct, then:
self.q_no += 1
if self.q_no==(self.data_size + 1):
#
# Do what you want to do if that was the last question.
#
else:
self.question_label['text'] = question[self.q_no]
#
# Here change the value of the buttons with the answers.
#
For the third question, what format do you exactly want? There are differences between .csv, .xlsx, .xlsb, etc. But for the conversion you can use the pandas module.
These methods need a specific type of object for the conversion, so be shure to format your data in that specific way before using them:
to csv to xlsx. This is the class you can use as data structure.
From personal experience I know that you can format your datas as json to convert them to csv, but I'm not shure if it works for xlsx and other formats too.
Hope I've helped a little bit.

Can anyone help me make this code more simple and not so lengthy?

**I am trying to make a hangman game but i feel like this could be simpler to code any suggestions?
buttonb = tkinter.Button(bg="blue", text = "B", width=2,command=ex)
buttonb.pack(side="left")
buttonc = tkinter.Button(bg="blue", text = "C",width=2, command=ex)
buttonc.pack(side="left")
buttond = tkinter.Button(bg="blue", text = "D", width=2,command=ex)
buttond.pack(side="left")
As #meowgoesthedog suggested a simple loop is your answer.
import string
buttons = {}
for letter in string.ascii_uppercase:
buttonb = tkinter.Button(bg = "blue", text = letter, width = 2,command = ex)
buttonb.pack(side = "left")
buttons[letter] = buttonb
You can use a loop to iterate over the letters and since the buttons belong together they can be stored in a data structure like a list.
buttons = []
for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
button = tkinter.Button(bg="blue", text = letter, width=2,command=ex)
button.pack(side="left")
buttons.append(button)
Feel free to use a list comprehension if you like.

Altering python variable identifiers in a loop which defines a tkinter button

I've got a function in a class in which I am creating several tkinter buttons within, where the number of buttons and the button properties depends on a file (so I can't create a specific number of variables to hold the buttons).
The code I have looks like this (sparing the complexities of the whole code):
import os
import tkinter as tk
Class(GUI):
def ButtonCreator(self):
self.HomeworkList = open("Files\HWNameList.txt", "r")
x = self.HomeworkList.readline()
while not x == "END":
x = x[0:-1]
HomeworkFileName = str("Files\HW-" + x + ".txt")
locals()["self.Button" + x] = tk.Button(master, text = x, command = lambda: self.DisplayHomeworkFile(FileName))
locals()["self.Button" + x].pack()
x = self.HomeworkList.readline()
self.HomeworkList.close()
def DisplayHomeworkFile(self, filename):
os.startfile(filename)
The file I am opening looks like this...
HomeworkName1
HomeworkName2
HomeworkName3
END
When the code runs, it displays the buttons with the correct text written on them but when i click on them they only ever display the file who's file name is written last in the HomeworkList file. Not sure what I've done wrong.
If there is another way to achieve what I'm trying I'm open to all suggestions.
Thanks.
This is a classic beginners problem that comes from misunderstanding how lambda works. For this case you need to use functools.partial.
You also need to forget about modifying locals(). Make a list or dictionary to hold the button instances.
from functools import partial
def ButtonCreator(self):
self.HomeworkList = open("Files\HWNameList.txt", "r")
x = self.HomeworkList.readline()
self.buttons = []
while not x == "END":
x = x[0:-1]
HomeworkFileName = str("Files\HW-" + x + ".txt")
btn = tk.Button(master, text = x, command = partial(self.DisplayHomeworkFile, HomeworkFileName))
btn.pack()
self.buttons.append(btn)
x = self.HomeworkList.readline()
self.HomeworkList.close()

How do I link imported questions with Imported answers?

I am a student at high-school and we are left to create a maths question answer game. We are given Questions and Answers in Excel to import into the program and it asks a random questions (from the ones given) and you have to be told if you have got the question correct or not.
I am stuck at this point where the program asks the question but I am unsure how to get the answer to match with the question given.
here is my code to see what I have done, any information helps.
csv_file = open("questions.csv", "r")
question_reader = csv.reader(csv_file, delimiter = ",")
for row in question_reader:
The_que = row[0]
The_ans = row[1]
self.questions.append(equation(The_que, The_ans))
csv_file.close()
for question in self.questions:
question.display()
from random import randint
ran_num = randint(0,20)
print(self.questions[ran_num].question)
random_question = (self.questions[ran_num].question)
self.label_1 = Label(parent, bg= "Black",
fg = "Pink",
text = random_question,
padx=30,
pady=10,
font =("times", "14", "bold " ))
self.label_1.pack()
self.button_1 = Button(parent,
text = "Lock in Answer",
command = self.Answer_entry)
self.button_1.pack()
self.entry_1 = Entry(parent)
self.entry_1.pack()
def Answer_entry(self):
the_text = self.entry_1.get()
self.label_1.configure(text = the_text)
So There is more to the code that I haven't linked but I think this will good to show. Right now I have
the_text
as a placeholder
. Reminder I am a High school student who started programming 3-5 weeks ago so please try to explain this like it is to a 5 year old since I am not great at this.

Categories

Resources