I'm trying to make a country quiz program.
I would like for the start GUI to completely be destroyed when the user clicks their difficulty they want to play.
However, I used foo.destroy() and the start GUI turns completely blank and doesn't disappear.
I tried using Toplevel() and while foo.destroy works the program opens up 2 guis when I run it.
from tkinter import *
from functools import partial
import csv
import random
import re
class Start:
def __init__(self):
# Color is light yellow for the background
background = "#FFF4C3"
# Start GUI
self.start_frame = Frame(padx=10, pady=10, bg=background)
self.start_frame.grid()
# Country Capital Quiz Heading row 0
self.capital_label = Label(self.start_frame, text="Country Capital Quiz",
font="Helvetica 30 bold", bg=background)
self.capital_label.grid(row=0)
# Sub text and Instructions for the game row 1
self.subtext_label = Label(self.start_frame, text="How well do you know world's capitals? \n\n"
"You'll be presented with 15 capitals from a list of 242 "
"capitals.\n "
"You'll need to match the capitals with their corresponding "
"country. \n\n "
"Please select the difficulty you wish to play.",
font="Arial 10", bg=background)
self.subtext_label.grid(row=1)
# to_game button frame row 2
self.to_game_frame = Frame(self.start_frame, bg=background)
self.to_game_frame.grid(row=2)
# Button Font
button_font = "Arial 15 bold"
# to_game buttons row 2.0
self.easy_button = Button(self.to_game_frame, text="Easy", font=button_font, bg="#99CCFF",
command=self.to_easy, height=2, width=13, borderwidth=2)
self.easy_button.grid(row=0, column=0, padx=10, pady=5)
self.hard_button = Button(self.to_game_frame, text="Hard", font=button_font, bg="#FFBAB8",
command=self.to_hard, height=2, width=13, borderwidth=2)
self.hard_button.grid(row=0, column=1, padx=10, pady=5)
# Help Button row 3
self.help_button = Button(self.start_frame, text="Help", font="Helvetica 10 bold", height=2, width=10,
borderwidth=3, command=self.help)
self.help_button.grid(row=3, pady=5)
def to_easy(self):
Easy()
self.start_frame.destroy()
def to_hard(self):
Hard()
self.start_frame.destroy()
def help(self):
get_help = Help(self)
get_help.help_text.configure(text="The quiz will present you with a capital \nYou must identify the "
"corresponding country.\n\nThere "
"are a total of 15 rounds.\n\n"
"Easy mode is a multiple choice quiz.\n"
"Hard mode you must type in the answer.\n\n"
"The answers can be case insensitive.\nHowever, they must have proper "
"spacing and spelling.")
class Help:
def __init__(self, partner):
background = "#FFF4C3"
# disable help button
partner.help_button.config(state=DISABLED)
# Sets up child window (ie: help box)
self.help_box = Toplevel()
# If users press 'x' cross at the top, closes help and 'releases' help button.
self.help_box.protocol('WM_DELETE_WINDOW', partial(self.close_help, partner))
# Set up GUI Frame
self.help_frame = Frame(self.help_box, bg=background)
self.help_frame.grid()
# Set up Help heading (row 0)
self.how_heading = Label(self.help_frame, text="Help / Information",
font=("Helvetica", "24", "bold",),
bg=background)
self.how_heading.grid(row=0)
# Help text (label, row 1)
self.help_text = Label(self.help_frame, text="", font="helvetica",
bg=background, justify=LEFT,wrap=350)
self.help_text.grid(row=1)
# Dismiss button (row 2)
self.dismiss_btn = Button(self.help_frame, text="Dismiss", width=10, bg="maroon", fg="white",
font="Helvetica" "10" "bold", command=partial(self.close_help, partner))
self.dismiss_btn.grid(row=2, pady=10)
def close_help(self, partner):
# Put help button back to normal...
partner.help_button.config(state=NORMAL)
self.help_box.destroy()
def to_quit():
root.destroy()
class Easy:
def __init__(self):
# Background color is light yellow
background = "#FFF4C3"
# Import the csv file, name of csv file goes here...
with open('country-capitals.csv', 'r') as f:
# make csv file into list
file = csv.reader(f)
next(f)
my_list = list(file)
# List to store the answers
self.game_history = []
# Initial Score
self.score = 0
# Amounts of games played
self.played = 0
# chooses four different countries / capitals from the list
question_ans = random.choice(my_list)
yes = random.choice(my_list)
no = random.choice(my_list)
ok = random.choice(my_list)
# Defining variables for the capitals and countries,
# question is the capital in question
# self.answer is the correct answer
# incorrect[1,2,3] are the incorrect countries.
self.question = question_ans[1]
self.answer = question_ans[0]
self.hint = question_ans[2]
incorrect1 = yes[0]
incorrect2 = no[0]
incorrect3 = ok[0]
# I made the button_list a list so the list can be randomized so that the answer button locations is always
# different.
button_list = [self.answer, incorrect1, incorrect2, incorrect3]
random.shuffle(button_list)
# Defining the randomized list to their corresponding buttons
self.top_left = button_list[0]
self.top_right = button_list[1]
self.bottom_left = button_list[2]
self.bottom_right = button_list[3]
# GUI Setup
self.game_box = Toplevel(bg=background)
self.game_frame = Frame(self.game_box, bg=background)
self.game_frame.grid()
self.game_box.protocol('WM_DELETE_WINDOW', to_quit)
# Capital Label row 0
self.capital_label = Label(self.game_frame, text=self.question,
font="Helvetica 15 bold", bg=background)
self.capital_label.grid(row=0)
# Label showing correct or incorrect row 1
self.answer_box = Label(self.game_frame, text="", font="Helvetica 12 italic", width=45, bg=background)
self.answer_box.grid(row=1)
# Setup grid for answer buttons row 2
self.top_answers_frame = Frame(self.game_box, width=50, height=50, bg=background)
self.top_answers_frame.grid(row=2, padx=5)
# width, wrap, font height for buttons
wt = 20
ht = 2
wr = 200
ft = "Helvetica 15"
# Top level answers buttons row 2.0
self.top_left_answer_button = Button(self.top_answers_frame, text=self.top_left,
font=ft, padx=5, pady=5, width=wt, height=ht, wrap=wr, bg="#EEE6D2",
command=lambda: self.reveal_answer(self.top_left))
self.top_left_answer_button.grid(column=0, row=0, padx=5, pady=5)
self.top_right_answer_button = Button(self.top_answers_frame, text=self.top_right,
font=ft, padx=5, pady=5, width=wt, height=ht, wrap=wr, bg="#EEE6D2",
command=lambda: self.reveal_answer(self.top_right))
self.top_right_answer_button.grid(column=1, row=0, padx=5, pady=5)
# Bottom level answers buttons row 2.1
self.bottom_left_answer_button = Button(self.top_answers_frame, text=self.bottom_left,
font=ft, padx=5, pady=5, width=wt, height=ht, wrap=wr, bg="#EEE6D2",
command=lambda: self.reveal_answer(self.bottom_left))
self.bottom_left_answer_button.grid(column=0, row=1, padx=5, pady=5)
self.bottom_right_answer_button = Button(self.top_answers_frame, text=self.bottom_right,
font=ft, padx=5, pady=5, width=wt, height=ht, wrap=wr, bg="#EEE6D2",
command=lambda: self.reveal_answer(self.bottom_right))
self.bottom_right_answer_button.grid(column=1, row=1, padx=5, pady=5)
# Label for the score and games played row 3
self.score_label = Label(self.game_box, text="{} correct, {} rounds played".format(self.score, self.played),
bg=background)
self.score_label.grid(row=3)
# Button frames for next and hint button row 4
self.button_frame = Frame(self.game_box, bg=background)
self.button_frame.grid(row=4)
# The hint button to get the hint for this country row 0 column 0
self.hint_button = Button(self.button_frame, text="Hint", command=self.to_hint, width=7,
font="Helvetica 10 bold")
self.hint_button.grid(row=0, column=0, padx=5,pady=8)
# The Next button to proceed to the next round row 0 column 1
self.next_button = Button(self.button_frame, text="Next",
command=lambda: self.to_next(my_list, self.game_history), width=7,
font="Helvetica 10 bold")
self.next_button.grid(row=0, column=1, padx=5,pady=8)
# Disable the next button initially,
self.next_button.config(state=DISABLED)
def reveal_answer(self, location):
# Disable all the buttons
self.top_left_answer_button.config(state=DISABLED)
self.top_right_answer_button.config(state=DISABLED)
self.bottom_left_answer_button.config(state=DISABLED)
self.bottom_right_answer_button.config(state=DISABLED)
self.hint_button.config(state=DISABLED)
# Enable the next_button
self.next_button.config(state=NORMAL)
# Increase total rounds played by 1
self.played += 1
# Check if button is correct.
if location == self.answer:
self.answer_box.config(text="Correct!", fg="green")
self.score += 1
correct_answer = "{}, the answer was {} \u2713 \n".format(self.question,self.answer)
self.game_history.append(correct_answer)
else:
self.answer_box.config(text="Incorrect, correct country is {}".format(self.answer), fg="red")
incorrect_answer = "{}, the answer was {} \u274c, you answered \n".format(self.question,self.answer,location)
self.game_history.append(incorrect_answer)
# Update the score that the user has
self.score_label.config(text="{} correct / {} rounds played".format(self.score, self.played))
def to_next(self, capital_list, history):
# if the amount of rounds played is 15 the player is taken to the end screen
if self.played == 15:
easy=1
End(self.score, history,easy)
self.game_box.destroy()
# Else the quiz repeats and new questions are asked.
else:
self.top_left_answer_button.config(state=NORMAL)
self.top_right_answer_button.config(state=NORMAL)
self.bottom_left_answer_button.config(state=NORMAL)
self.bottom_right_answer_button.config(state=NORMAL)
self.next_button.config(state=DISABLED)
self.answer_box.config(text="")
self.hint_button.config(state=NORMAL)
# chooses four different countries / capitals from the list
question_ans = random.choice(capital_list)
yes = random.choice(capital_list)
no = random.choice(capital_list)
ok = random.choice(capital_list)
# Defining variables for the capitals and countries,
# question is the capital in question
# self.answer is the correct answer
# incorrect[1,2,3] are the incorrect countries.
self.question = question_ans[1]
self.answer = question_ans[0]
self.hint = question_ans[2]
incorrect1 = yes[0]
incorrect2 = no[0]
incorrect3 = ok[0]
self.capital_label.config(text=self.question)
# I made the button_list a list so the list can be randomized so that the answer button locations is always
# different.
button_list = [self.answer, incorrect1, incorrect2, incorrect3]
random.shuffle(button_list)
self.top_left = button_list[0]
self.top_right = button_list[1]
self.bottom_left = button_list[2]
self.bottom_right = button_list[3]
# Defining the randomized list to their corresponding buttons
self.top_left_answer_button.config(text=self.top_left, command=lambda: self.reveal_answer(self.top_left))
self.top_right_answer_button.config(text=self.top_right, command=lambda: self.reveal_answer(self.top_right))
self.bottom_left_answer_button.config(text=self.bottom_left,
command=lambda: self.reveal_answer(self.bottom_left))
self.bottom_right_answer_button.config(text=self.bottom_right,
command=lambda: self.reveal_answer(self.bottom_right))
def to_hint(self):
get_hint = Hint(self)
get_hint.help_text.configure(text="The country is located in: {}".format(self.hint))
class Hard:
def __init__(self):
# Background color is light yellow
background = "#FFF4C3"
# Game History List
self.game_history = []
# Import the csv file, name of csv file goes here...
with open('country-capitals.csv', 'r') as f:
# make csv file into list
file = csv.reader(f)
next(f)
my_list = list(file)
# choose an item from the main list, this item is itself a list
question_ans = random.choice(my_list)
# Initial Score
self.score = int(0)
# Amounts of games played
self.played = 0
# first item in small list
self.question = question_ans[1]
self.answer = question_ans[0]
self.hint = question_ans[2]
# GUI Setup
self.game_box = Toplevel()
self.game_frame = Frame(self.game_box, bg=background)
self.game_frame.grid()
self.game_box.protocol('WM_DELETE_WINDOW', to_quit)
# Capital Label row 0
self.capital_label = Label(self.game_frame, text=self.question,
font="Helvetica 15 bold", bg=background)
self.capital_label.grid(row=0, padx=5, pady=10)
# Setup Answer Entry row 1
self.answer_entry = Entry(self.game_frame, font="Helvetica 15 bold")
self.answer_entry.grid(row=1, pady=10, padx=30)
self.answer_entry.focus()
self.answer_entry.bind('<Return>', lambda e: self.check_answer())
# Button frame for "guess" and "next" row 2
self.button_frame = Frame(self.game_frame, bg=background)
self.button_frame.grid(row=2)
# Button to press when users have entered the country row 2.0 column 0
self.answer_button = Button(self.button_frame, text="Guess", font="Helvetica 10 bold",
command=lambda: self.check_answer())
self.answer_button.grid(row=0, column=0, padx=5)
# The hint button to get the hint for this country row 2 column 1
self.hint_button = Button(self.button_frame, text="Hint", command=self.to_hint, width=5,
font="Helvetica 10 bold")
self.hint_button.grid(row=0, column=1, padx=5)
# Button to go to the next question row 2.0 column 2
self.next_button = Button(self.button_frame, text="Next", font="Helvetica 10 bold",
command=lambda: self.next_question(my_list, self.game_history))
self.next_button.grid(row=0, column=2, padx=5)
self.next_button.config(state=DISABLED)
self.next_button.bind('<Return>', lambda e: self.next_question(my_list, self.game_history))
# Correct or incorrect Label row 3
self.answer_box = Label(self.game_frame, text="", font="Helvetica", bg=background, width=35, wrap=170)
self.answer_box.grid(row=3)
# Total amount of correct answers and games played row 4
self.points = Label(self.game_frame, text="{} correct / {} rounds played".format(self.score, self.played),
font="Helvetica 10", bg=background)
self.points.grid(row=4)
def check_answer(self):
user_answer = self.answer_entry.get()
self.hint_button.config(state=DISABLED)
self.played += 1
if user_answer.casefold() == self.answer.casefold():
self.answer_box.config(text="Correct!", fg="green")
self.score += 1
self.answer_entry.config(bg="#ACF392")
# History to be appended if correct
guess_history_correct = \
"{}, the answer was {} \u2705 \n".format(self.question,self.answer)
self.game_history.append(guess_history_correct)
else:
self.answer_box.config(text="The country is located in {}".format(self.answer), fg="#F62121")
self.answer_entry.config(bg="#F39292")
# History to be appended if incorrect
guess_history_incorrect = \
"{}, the answer was {} \u274c , you answered {} \n".format(self.question,self.answer,user_answer)
self.game_history.append(guess_history_incorrect)
self.next_button.config(state=NORMAL)
self.answer_button.config(state=DISABLED)
self.next_button.focus()
self.points.config(text="{} correct / {} rounds played".format(self.score, self.played))
def next_question(self, capital_list, guesses):
# When the user has played 15 rounds we take them to the end gui.
if self.played == 15:
hard=0
End(self.score, guesses,hard)
self.game_box.destroy()
# If they amount of played is not 15 new questions are generated.
else:
question_ans = random.choice(capital_list)
self.question = question_ans[1]
self.answer = question_ans[0]
self.hint = question_ans[2]
self.capital_label.config(text=self.question)
self.answer_entry.delete(0, "end")
self.answer_box.config(text="")
self.next_button.config(state=DISABLED)
self.answer_button.config(state=NORMAL)
self.answer_entry.config(bg="white")
self.answer_entry.focus()
self.hint_button.config(state=NORMAL)
def to_hint(self):
get_hint = Hint(self)
get_hint.help_text.configure(text="The country is located in: {}".format(self.hint))
if __name__ == "__main__":
root = Tk()
root.title("Country Quiz")
something = Start()
root.mainloop()
Related
In my code, I have tried to get the user input through text fields, store them in variables and finally print them in a tabular form.
The problem I am facing is that none of the values I enter through the text fields get displayed; when I try printing the variables, they come up empty.
Here's part of my code:
# SPASC
from tkinter import *
import tkinter as tk
import tkinter.ttk as tktrv
root = tk.Tk()
root.title("SPASC")
root.geometry("410x400")
lb1 = Label(root, text="SPASC \n Welcomes You !!!", fg="red", bg="sky blue"
, font=('Arial Black', 20), width=22, anchor=CENTER)
lb2 = Label(root, text="What would you like to compare?",
font=('Arial', 18), anchor=CENTER)
space1 = Label(root, text="\n\n")
lb1.grid(row=0)
lb2.grid(row=5)
space1.grid(row=1)
hpw, mil = StringVar(), StringVar()
def bt_cars():
w1 = Toplevel()
w1.title("Choose Features")
w1.geometry("430x200")
lb3 = Label(w1, text="Choose features for comparison", bg="yellow"
, font=('Arial Black', 18), width=25)
lb4 = Label(w1, text=" ", anchor=CENTER)
fr1 = LabelFrame(w1, width=20, padx=100)
hpw_cb = Checkbutton(fr1, text="Horsepower", variable=hpw, anchor='w', onvalue="Horsepower", offvalue="")
hpw_cb.grid()
hpw_cb.deselect()
mil_cb = Checkbutton(fr1, text="Mileage", variable=mil, anchor='w', onvalue="Mileage", offvalue="")
mil_cb.grid()
mil_cb.deselect()
var_stor = [hpw, mil]
print(hpw)
print(mil)
var_fill = []
for itr1 in var_stor:
if itr1 != "":
var_fill.append(itr1)
print(var_fill)
def car_1():
name1 = StringVar()
c1 = Toplevel()
c1.title("Car 1")
c1.geometry("430x200")
car1_lb1 = Label(c1, text="Car Name:")
name1_ifl = Entry(c1)
name1 = name1_ifl.get()
elm_var_fill = len(var_fill)
ct1 = 0
car1_val = []
for itr2 in var_fill:
if ct1 == elm_var_fill:
break
lb5 = Label(c1, text=itr2.get())
#Creating text field
ftr1_ifl = Entry(c1)
car1_ftr = ftr1_ifl.get()
car1_val.append(car1_ftr)
car1_ftr = None
lb5.grid(row=ct1 + 2, column=1)
ftr1_ifl.grid(row=ct1 + 2, column=2)
ct1 += 1
print(car1_val)
def display():
dp = Toplevel()
dp.title("Results")
dp.geometry("500x200")
car1_pt = 0
car2_pt = 0
car_tree = tktrv.Treeview(dp)
car_tree["columns"] = ("car1col")
car_tree.column("#0", width=120, minwidth=30)
car_tree.column("car1col", width=120, minwidth=30)
car_tree.heading("#0", text="Features" )
car_tree.heading("car1col", text=str(name1))
car_tree.pack()
c1.withdraw()
print(var_fill)
done1_bt = Button(c1, text="Continue", command=display)
name1_ifl.grid(row=0, column=2)
car1_lb1.grid(row=0, column=1)
done1_bt.grid(row=5,column=1)
w1.withdraw()
done_bt = Button(w1, text="Done", command=car_1)
done_bt.grid(row=3, column=1)
lb3.grid(row=0, column=1)
lb4.grid(row=1, column=1)
fr1.grid(row=2, column=1)
root.withdraw()
bt1 = Button(root, text="CARS", width=5, font=('Calibri', 15), command=bt_cars)
bt1.grid(row=7)
space2 = Label(root, text="\n\n")
space2.grid(row=6)
root.mainloop()
I am facing trouble with the variables named: hpw, mil, name1.
Any help would be welcome.
NOTE:- Please excuse the amount of code; I wanted others to replicate the error and see it for themselves
For the variables hpw and mil, these variables are empty strings that's why you are not getting any value from those checkboxes. To get values from the checkboxes replace these lines of code:
var_stor = [hpw, mil]
with
var_stor = [hpw_cb.cget('onvalue'), mil_cb.cget('onvalue')]
since you want the onvalue then you must use cget() method to access those values.
also, replace
lb5 = Label(c1, text=itr2.get())
with
lb5 = Label(c1, text=itr2)
because now you have required values (not objects) in a list, so just need to access those values.
For the variable name1 you can use #BokiX's method.
The problem is you are using get() wrong. You cannot use get() right after Entry() because as soon as entry is created it's getting the input before the user can even input something.
Use this code:
def get_input(text):
print(text)
e = Entry(root)
e.pack()
b = Button(root, text="Print input", command=lambda: get_input(e.get()))
b.pack()
Now get() method will not be executed before you click the button.
I work on a program that calculates the macros of each meal. You can enter a value in gram and it calculates for each aliment your intake. I would like now to add these values together when I push multiple buttons. Then I'll display the value somewhere and maybe I could do a graph after.
Here I show what my program looks like :
import tkinter as tk
def value_kiwi():
value = ent_quantity.get()
formula = (float(value)/100)
cal = 53
pro = 1.6
glu = 11.1
li = 0.3
Calories = (cal) * formula
Protéines = (pro) * formula
Glucides = (glu) * formula
Lipides = (li) * formula
lbl_cal["text"] =f"{Calories}"
lbl_prot["text"] = f"{Protéines}"
lbl_glu["text"] = f"{Glucides}"
lbl_lip["text"] = f"{Lipides}"
def value_banane():
value = ent_quantity.get()
formula = (float(value)/100)
cal = 90
pro = 1.5
glu = 20.1
li = 0
Calories = (cal) * formula
Protéines = (pro) * formula
Glucides = (glu) * formula
Lipides = (li) * formula
lbl_cal["text"] =f"{Calories}"
lbl_prot["text"] = f"{Protéines}"
lbl_glu["text"] = f"{Glucides}"
lbl_lip["text"] = f"{Lipides}"
window = tk.Tk()
window.title("Calculateur de Calories et Nutriments")
frm_entry = tk.Frame(master=window)
ent_quantity = tk.Entry(master=frm_entry, width=5)
ent_quantity.grid(row=1, column=0,)
lbl_cal = tk.Label(master=window)
lbl_cal.grid(row=1, column=1,)
lbl_prot = tk.Label(master=window)
lbl_prot.grid(row=1, column=2)
lbl_glu = tk.Label(master=window)
lbl_glu.grid(row=1, column=3)
lbl_lip = tk.Label(master=window)
lbl_lip.grid(row=1, column=4)
btn_kiwi = tk.Button(
master=window,
text="Kiwi",
command=value_kiwi,
)
btn_banane = tk.Button(
master=window,
text="Banane",
command=value_banane,
)
lbl_calories = tk.Label(master=window, text="Calories",)
lbl_proteines = tk.Label(master=window, text="Protéines")
lbl_glucides = tk.Label(master=window, text="Glucides")
lbl_lipides = tk.Label(master=window, text="Lipides")
lbl_fruits = tk.Label(master=window, text="Fruits")
frm_entry.grid(row=1, column=0, padx=10)
lbl_calories.grid(row=0,column=1, padx=5, sticky="w")
lbl_proteines.grid(row=0, column=2, padx=10)
lbl_glucides.grid(row=0, column=3, padx=10)
lbl_lipides.grid(row=0,column =4, padx=10)
lbl_fruits.grid(row=1,column =5, padx=10)
btn_kiwi.grid(row=2, column=5, pady=10)
btn_banane.grid(row=3, column=5, pady=10)
window.mainloop()
Ok, I think I improved Your code:
from tkinter import Tk, Button, Entry, Label
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.title('Nutrient calculator')
# entry
Label(self, text='Amount').grid(row=0, column=0)
self.amount = Entry(self, width=5, bg='light grey')
self.amount.grid(row=1, column=0)
# calories
Label(self, text='Calories').grid(row=0, column=1)
self.calories = Label(self)
self.calories.grid(row=1, column=1)
# proteins
Label(self, text='Proteins').grid(row=0, column=2)
self.proteins = Label(self)
self.proteins.grid(row=1, column=2)
# glucose
Label(self, text='Glucose').grid(row=0, column=3)
self.glucose = Label(self)
self.glucose.grid(row=1, column=3)
# lipids
Label(self, text='Lipids').grid(row=0, column=4)
self.lipids = Label(self)
self.lipids.grid(row=1, column=4)
# list of all labels
self.data_labels = [self.calories, self.proteins, self.glucose, self.lipids]
# error label for if no value is entered or it cannot be converted
self.error_label = Label(self, text='Use integer or float value!')
# stuff for adding multiple foods
self.do_add = False
self.nutrient_list = []
self.plus_btn = Button(self, text='Add', command=self.add)
self.plus_btn.grid(row=3, column=0, columnspan=4, sticky='nwes')
def display_values(self, value_list):
self.nutrient_list.append(value_list)
if len(self.nutrient_list) == 2 and not self.do_add:
self.nutrient_list.pop(0)
elif len(self.nutrient_list) > 2 and not self.do_add:
self.nutrient_list = [self.nutrient_list[-1]]
print(self.nutrient_list)
if self.do_add:
value_list = []
for i in range(len(self.nutrient_list[0])):
total_value = 0
for item in self.nutrient_list:
total_value += item[i]
value_list.append(total_value)
for text, label in zip(value_list, self.data_labels):
label.config(text=f'{text:.2f}')
self.do_add = False
def handle_value_error(self):
self.error_label.grid(row=2, column=0, columnspan=4)
def add(self):
self.do_add = True
self.amount.delete(0, 'end')
class Values:
def __init__(self, name, row, column, calories, proteins, glucose, lipids):
self.parent = root
self.name = name
self.row = row
self.column = column
self.calories = calories
self.proteins = proteins
self.glucose = glucose
self.lipids = lipids
self.stuff_list = [self.calories, self.proteins, self.glucose, self.lipids]
self.button = Button(self.parent, text=self.name, command=self.set_values)
self.button.grid(row=self.row, column=self.column, sticky='nwse')
def set_values(self):
value_list = []
value_ = self.parent.amount.get()
try:
formula = float(value_) / 100
self.parent.error_label.grid_forget()
for item in self.stuff_list:
item *= formula
value_list.append(item)
self.parent.display_values(value_list)
except ValueError:
self.parent.handle_value_error()
root = MainWindow()
kiwi = Values('Kiwi', 2, 4, calories=53, proteins=1.6, glucose=11.1, lipids=0.3)
banana = Values('Banana', 3, 4, calories=90, proteins=1.5, glucose=20.1, lipids=0)
root.mainloop()
it is not necessarily better however a few benefits from using this code are:
better organization (IMO) since it uses classes which make it pretty neat (however You have to have an understanding over them)
takes less space - what I mean is that now, to define a new food, You just have to initiate the Values class and give it the necessary arguments. You can take an example from the given instances.
possibly easier to expand (both because better organization and takes less space)
I edited the code so now it should have that functionality, the way it works is You choose an amount , then fruit and if You want to add more, press add button, then choose amount, press food and it will display the total so far, then again, if You want to add more press add and then put in an amount and press the fruit, currently the way to 'clear' is to press any food button and it should then reset the whole list and keep only the one that was just pressed and then repeat.
I made a Tkinter quiz application, and the code is below. The MainQuizApp.py controls the main flow, and the questions_model.py models reading the quiz questions from a .csv file. Right now, my code is hardcoded where everything comes from the questions.csv file I created while writing my program, but I'd like for my app to let the user type in which .csv file they'd like the quiz app to load questions from. Right now, the entry widget on the main screen would be where a user would type in the .csv file name (it's read-only/hardcoded to questions.csv at the moment), and after hitting Load and Start, the quiz app would run with those questions instead of always questions.csv. Any help with how I can make this happen?
MainQuizApp.py
import questions_model as qm
# import tkinter
from tkinter import *
import tkinter as tk
from tkinter import messagebox # necessary for the Instructions dialogue box
from tkinter import ttk # necessary for the Progressbar widget
# import time
import time
import datetime
# create MainQuizApp class
class MainQuizApp():
def __init__(self, questions_file='questions.csv'):
self.questions_file = questions_file
self._build_gui() # build_gui function
self.clock() # run clock function
self.current_question_index=0 # question index counter
self.points = 0 # number points counter
self.num_right = 0 # number questions correct counter
self.tries = 2 # number tries counter
self.user_answer = tk.IntVar()
self.root.mainloop()
# show instructions for StudyStar⭐️
def about(self):
messagebox.showinfo(title="About StudyStar⭐️", message="Put instructions here!")
return
# question from .csv file
def get_question(self):
# print(self.questions[self.current_question_index]) #DEBUG
return self.questions[self.current_question_index][0]
# options from .csv file
def get_options(self):
print(self.current_question_index)
print(self.questions[self.current_question_index])
return self.questions[self.current_question_index][1:-1]
# correct answer from .csv file
def get_current_answer(self):
return self.questions[self.current_question_index][-1]
# start showing questions
def start(self):
self.questions = qm.get_questions(self.questions_file)
current_question = self.get_question()
self._update_question(current_question)
options = self.get_options()
self._make_options(options)
self.qset_name_button.grid_forget() # hide load and start button
self.qset_name_entry.grid_forget() # hide qset name text entry widget
self.qset_name_label.grid_forget() # hide qset name label
self.qset_filename_label.grid(row=15) # show reading from filename
self.submit_button.grid(row=6) # show check answer button
self.questions_box.grid(row=2,column=0) # show questions box
# progress bar
self.my_progress = ttk.Progressbar(self.root, orient=HORIZONTAL,length=400, mode='determinate')
self.my_progress.grid(row=12, column=0, columnspan=3, padx=20, pady=10)
self.number_of_questions = len(self.questions) #total number questions in set
self.points_possible = self.number_of_questions * 10 #total points possible
# increase step of progress bar
def step(self):
self.my_progress['value'] += self.number_of_questions
# is answer correct?
def is_answer_correct(self):
return self.get_current_answer() == self.get_options()[self.user_answer.get()-1]
# update answer label correct/incorrect
def _update_answer_label(self):
while True:
if self.is_answer_correct():
status = 'Correct! +10 points!'
self.answer_label.config(text=status, font='Helvetica 14 bold', fg='#ffbb00')
self.points = self.points + 10
self.num_right = self.num_right + 1
stop_asking = False
self.tries = 2
break
else:
status = 'Incorrect! -5 points! You have 1 more try.'
self.points = self.points - 5
self.answer_label.config(text=status,font='Helvetica 14 bold', fg='#ffbb00')
self.tries = self.tries - 1
if self.tries == 0:
status = 'Incorrect! -5 points! No tries left.'
status += ' The correct answer is: {}'.format(self.get_current_answer())
self.answer_label.config(text=status, fg='#ffbb00', font='Helvetica 14 bold')
self.points = self.points - 5
stop_asking = True
self.tries = 2
break
if stop_asking:
self.tries = 2
break
def _update_questions_remaining(self):
pass
def _update_question(self,question):
self.questions_label.config(text=question)
def get_next_question(self):
self.current_question_index += 1
if self.current_question_index == len(self.questions):# if last question has been answered
#self._update_question.grid_forget()
self.questions_box.grid_forget() # hide questions box
self.my_progress.grid_forget() # hide progress bar
self.qset_filename_label.grid_forget() # hide reading from filename text
self.study_again_button.grid(row=2, column=1) # show study again button
tk.Label(self.root, text="Congratulations - you finished the quiz! ",font='Helvetica 16 bold', bg='#375e97', fg='#ffbb00').grid(row=3) # show questions correct
tk.Label(self.root, text=str(self.num_right) + " " + "out of" + " " + str(self.number_of_questions) + " " + "questions were answered correctly.",font='Helvetica 16 bold', bg='#375e97', fg='white').grid(row=4)
tk.Label(self.root, text="Total number of points:" + " " + str(self.points) + " " + "out of" + " " + str(self.points_possible),font='Helvetica 16 bold', bg='#375e97', fg='white').grid(row=5) #show total points
return
else:
self._update_questions_remaining()
self._update_question(self.get_question())
self._update_options(self.get_options())
# check answer and update label
def check_and_update(self):
self._update_answer_label()
self.root.after(1500,self.get_next_question)
# build GUI components
def _build_gui(self):
self.root = tk.Tk()
self.root.geometry('600x300') # window size
self.root.title("StudyStar⭐️") # window title
self.root.configure(background = '#375e97') # set background color
self.welcome_label = tk.Label(self.root, text="StudyStar⭐️", font='Helvetica 18 bold', bg='#375e97', fg='white').grid(row=1, column=0, padx=20)
self.user_answer = tk.IntVar()
self.questions_box = tk.Frame(self.root, bg='#375e97')
self.questions_label = tk.Label(self.questions_box, bg='#375e97',fg='#ffbb00', font='Helvetica 20 bold')
self.questions_label.grid(row=0)
self.option_buttons = []
self.answer_label = tk.Label(self.questions_box, bg='#375e97')
self.answer_label.grid(row=5)
self.qset_name_label = tk.Label(self.root, text="Enter the name of your .csv file:", bg='#375e97', fg='white')
self.qset_name_label.grid(row=2)
self.qset_name_entry = Entry(self.root, width=20, highlightbackground='#375e97') # I DO need the entry widget as attribute so I can get it's text later!
self.qset_name_entry.insert(0, "questions.csv") # default text
self.qset_name_entry.config(state=DISABLED) # make text entry read only
self.qset_name_entry.grid(row=3, column=0)
self.qset_name_button = Button(self.root, text="Load and Start", font='Helvetica 16', fg='#3f681c', highlightbackground='#375e97', command=self.start)
self.qset_name_button.grid(row=3, column=1, columnspan=1)
self.filename = self.qset_name_entry.get()
self.qset_filename_label = tk.Label(self.root, text= "Reading questions from:" + " " + self.filename, bg='#375e97', fg='white')
# Submit button
self.submit_button = tk.Button(self.questions_box, text="Check Answer", font='Helvetica 16 bold', fg='#3f681c', highlightbackground='#375e97', command=lambda:[self.check_and_update(), self.step()])
# Start Quiz button
self.side_box = tk.Frame(self.root, bg='#375e97')
#self.start_button = tk.Button(self.side_box, text="Begin Studying",font='Helvetica 16', fg='#3f681c', highlightbackground='#375e97', command=self.start)
#self.start_button.grid(row=3)
#self.side_box.grid(row=3,column=1)
# Quit Quiz button
self.quit_button = tk.Button(self.root, text="Quit Quiz", font='Helvetica 16', fg='red', highlightbackground='#375e97', command=self.root.quit)
self.quit_button.grid(row=0, column= 1)
# About Quiz button
self.about_button = tk.Button(self.root, text="Instructions", font='Helvetica 16', highlightbackground='#375e97', command=self.about)
self.about_button.grid(row=1, column=1)
# Study Again button
self.study_again_button = tk.Button(self.root, text="Study Again",font='Helvetica 16', fg='#3f681c', highlightbackground='#375e97', command=None) #TODO
# clock
def clock(self):
self.hour = time.strftime("%H") # hour
self.minute = time.strftime("%M") # minute
self.second = time.strftime("%S") # second
self.am_pm = time.strftime("%p") # am/pm
self.clock_label = tk.Label(self.root, text="", font='Helvetica 16 bold', fg="black", bg="#ffbb00")
self.clock_label.grid(row=0, column=0, padx=20)
self.clock_label.config(text=self.hour + ":" + self.minute + ":" +self.second + " " + self.am_pm)
self.clock_label.after(1000, self.clock) # update clock
def _make_options(self,options_list):
for option,index in zip(options_list,range(len(options_list))):
self.option_buttons.append(tk.Radiobutton(self.questions_box, text=option, font='Helvetica 16', bg='#375e97',fg='white', value=index+1, variable=self.user_answer))
self.option_buttons[-1].grid(row=index+1)
def _update_options(self,options_list):
self.user_answer.set(7)
for option_button,option,index in zip(self.option_buttons,options_list,range(len(options_list))):
option_button.config(text=option, value=index+1)
# run app
# Create an object of the MainQuizApp class to begin the program
if __name__ == '__main__':
MainQuizApp()
questions_model.py
import csv
import random
def get_questions(filename):
questions = None
try:
with open(filename,'r') as file:
reader = csv.reader(file)
next(reader, None) # skip the headers
questions = list(reader)
lines = len(questions)
random.shuffle(questions) # randomize question order
except:
print("unable to process request")
return questions
if __name__ == '__main__':
print(get_questions('questions.csv'))
I am writing a quiz program in tkinter that has a frame for each question being asked. After the user selects the correct answer from a radiobutton list, a window pops up allowing the user to select any of the questions in the quiz. Here is an example:
Instead of showing all of the possible answers, I'd like to have just a "next" and "previous" button but I'm having difficulty creating a button that would self update every time the next or previous button was selected.
Here is my complete code below for reference. I've highlighted the important parts afterwards to make it a little easier.
Root_File_Name = "C:\\LearningArabic\\LiblibArriby\\"
def Part1():
JSON_File = Root_File_Name + "Lessons\\Lesson_1\\"
with open(JSON_File+"Arabic_Lesson_1.json", "r", encoding = "utf-8-sig") as question_file:
data = json.load(question_file)
def create_widgets_in_first_frame(): # Create the label for the frame
current_frame=frames[0] #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
question_number = "question0"
question_frame_populator(current_frame, question_number)
def create_widgets_in_second_frame():
current_frame=frames[1] #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
question_number = "question1"
question_frame_populator(current_frame, question_number)
def create_widgets_in_third_frame():
current_frame=frames[2] #Make the frame number generic so as to make copy/paste easier
question_number = "question2"
question_frame_populator(current_frame, question_number)
def create_widgets_in_forth_frame():
current_frame=frames[3] #Make the frame number generic so as to make copy/paste easier
question_number = "question3"
question_frame_populator(current_frame, question_number)
def create_widgets_in_fifth_frame():
current_frame=frames[4] #Make the frame number generic so as to make copy/paste easier
question_number = "question4"
question_frame_populator(current_frame, question_number)
def create_widgets_in_sixth_frame():
current_frame=frames[5] #Make the frame number generic so as to make copy/paste easier
question_number = "question5"
question_frame_populator(current_frame, question_number)
def question_frame_populator(current_frame, question_number): #This is what displayes all of the information on the frame
questionDirectory = data["lesson 1"]["part one"][question_number] ##UPDATE PER QUESTION## This is the directory for the question.
wronganswer = questionDirectory["wronganswer"] #This is the directory for the wrong answers
question = questionDirectory.get("question") #This is the question text
correctanswer = questionDirectory.get("answer") #This is the answer for whichever question has been selected.
arabic = questionDirectory.get("arabic") #This is the arabic text for the question
transliteration = questionDirectory.get("transliteration")
global var
var = IntVar()
var.set(0) #Sets the initial radiobutton selection to nothing
answers = generate_answers(wronganswer, correctanswer) #Pulls answers generated from the "generate_answers" function
choices = []
for i in range(3):
choice = Radiobutton(current_frame, image=answers[i], variable = var, value=i+1, command= Check_Answer)
choice.image = answers[i]
choices.append(choice)
random.shuffle(choices) #This line of code randomizes the order of the radiobuttons.
choices[0].grid(row=1, column=0)
choices[1].grid(row=1, column=1)
choices[2].grid(row=1, column=2)
L1 = Label(current_frame, text=question, font=("Helvetica", 35)) #This displays the question at the top of the screen
L1.grid(columnspan=4, row=0)
transliteration_button = Button(current_frame, text="Show Transliteration", command= lambda: Transliteration(current_frame, arabic, transliteration)) # Makes the phonetic pronunciation button. #####
transliteration_button.grid(column=0, row=2)
Previous_Button() # Creates the "previous" button and displays it.
Next_Button() # Creates the "next" button and displays it.
Quit_Button(current_frame) # Creates the "quit" button and displays it.
def Transliteration(current_frame, arabic, transliteration):
Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
Transliteration.grid(row=3, columnspan=4)
def generate_answers(wronganswer, correctanswer):
Wans=random.sample(wronganswer, 2)
images = [os.path.join(ImagePath, f"{Wans[i]}.png") for i in range(2)]
images += [os.path.join(ImagePath, f"{correctanswer}.png")]
answers = [PhotoImage(file=images[i]) for i in range(3)]
return answers
def Check_Answer():
global lives
global score
if str(var.get()) !="3":
special_frames[1].grid_forget() #This is the frame for right answers
special_frames[0].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for wrong answers
lives -=1
Incorrect = Label(special_frames[0], text ="That's incorrect!\n Lives: " +str(lives) + "\n Score: " + str(score), font=("Helvetica", 35))
Incorrect.grid(row=0, rowspan=2, column=0, columnspan=3)
if str(var.get()) == "3":
score +=1
special_frames[0].grid_forget() #This is the frame for wrong answers
special_frames[1].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for right answers
Correct = Label(special_frames[1], text = " That's right! \n Lives: " +str(lives)+ "\n Score: " + str(score), font=("Helvetica", 35))
Correct.grid(row=0, rowspan=2, column=0, columnspan=5)
first_frame_button = Button(special_frames[1], text = "Question 1", command = call_frame_1)
first_frame_button.grid(column=0, row=3)
second_frame_button = Button(special_frames[1], text = "Question 2", command = call_frame_2)
second_frame_button.grid(column=1, row=3)
third_frame_button = Button(special_frames[1], text = "Question 3", command = call_frame_3)
third_frame_button.grid(column=2, row=3)
forth_frame_button = Button(special_frames[1], text = "Question 4", command = call_frame_4)
forth_frame_button.grid(column=4, row=3)
fifth_frame_button = Button(special_frames[1], text = "Question 5", command = call_frame_5)
fifth_frame_button.grid(column=0, row=4)
sixth_frame_button = Button(special_frames[1], text = "Question 6", command = call_frame_6)
sixth_frame_button.grid(column=1, row=4)
def all_frames_forget():
for i in range(6): #This is for question frames
frames[i].grid_forget()
for i in range(3): #This is for special frames, like the correct and incorrect answer frames
special_frames[i].grid_forget()
def check_remaining_lives(create_widgets_in_current_frame, current_frame):
if lives<= 0:
Zero_Lives()
else:
create_widgets_in_current_frame
current_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
def Zero_Lives():
special_frames[2].grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
L5 = Label(special_frames[2], text="You have no remaining lives. \nPlease quit the lesson and try again.", font=("Helvetica", 35))
L5.grid(columnspan=4, row=0)
quit_button = Button(special_frames[2], text = "Quit", command = root_window.destroy)
quit_button.grid(column=1, columnspan = 2, row=2)
def Previous_Button():
previous_button = Button(special_frames[1], text = "Previous", command = previous_question)
previous_button.grid(column=1, row=5)
def previous_question():
global frameNumber
frameNumber -=1
call_frame_frameNumber()
def Next_Button():
next_button = Button(special_frames[1], text = "Next", command = next_question)
next_button.grid(column=2, row=5)
def next_question():
global frameNumber
frameNumber +=1
call_frame_frameNumber()
def Quit_Button(current_frame):
quit_button = Button(current_frame, text = "Quit", command = quit_program)
quit_button.grid(column=4, row=4)
def quit_program():
root_window.destroy()
def call_frame_1():
all_frames_forget()
create_widgets_in_current_frame = create_widgets_in_first_frame() #This line is unique
current_frame = frames[0] #This line is unique
check_remaining_lives(create_widgets_in_current_frame, current_frame)
def call_frame_2():
all_frames_forget()
create_widgets_in_current_frame = create_widgets_in_second_frame() #This line is unique
current_frame = frames[1] #This line is unique
check_remaining_lives(create_widgets_in_current_frame, current_frame)
def call_frame_3():
all_frames_forget()
create_widgets_in_current_frame = create_widgets_in_third_frame() #This line is unique
current_frame = frames[2] #This line is unique
check_remaining_lives(create_widgets_in_current_frame, current_frame)
def call_frame_4():
all_frames_forget()
create_widgets_in_current_frame = create_widgets_in_forth_frame() #This line is unique
current_frame = frames[3] #This line is unique
check_remaining_lives(create_widgets_in_current_frame, current_frame)
def call_frame_5():
all_frames_forget()
create_widgets_in_current_frame = create_widgets_in_fifth_frame() #This line is unique
current_frame = frames[4] #This line is unique
check_remaining_lives(create_widgets_in_current_frame, current_frame)
def call_frame_6():
all_frames_forget()
create_widgets_in_current_frame = create_widgets_in_sixth_frame() #This line is unique
current_frame = frames[5] #This line is unique
check_remaining_lives(create_widgets_in_current_frame, current_frame)
##### Program starts here #####
Lesson1_FilePath = Root_File_Name + "Lessons\\Lesson_1\\"
ImagePath = Lesson1_FilePath + "Images\\"
root_window = Tk() # Create the root GUI window.
root_window.title("Lesson 1: Part 1") # Label the root GUI window.
global score
score = 0 #Setting the initial score to zero.
global lives
lives = 3 #Setting the initial number of lives.
global frameNumber
frameNumber = 1
window_width = 200 # Define window size
window_heigth = 100
frames = [] # This includes frames for all questions
for i in range(6):
frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
frame['borderwidth'] = 2
frame['relief'] = 'sunken'
frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
frames.append(frame)
special_frames=[] #This includes the frames for: wrong answers, right answers, and zero lives
for i in range(3):
special=tkinter.Frame(root_window, width=window_width, height=window_heigth)
special['borderwidth'] = 2
special['relief'] = 'sunken'
special.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
special.grid_forget()
special_frames.append(special)
call_frame_1() #Calls the first function which creates the firist frame
root_window.mainloop()
I know it's a lot of code but I wanted to make sure everything was there in case something wasn't making sense to you all. Here are the parts that I think are the most important:
def Previous_Button():
previous_button = Button(special_frames[1], text = "Previous", command = previous_question)
previous_button.grid(column=1, row=5)
def previous_question():
global frameNumber
frameNumber -=1
call_frame_frameNumber()
def Next_Button():
next_button = Button(special_frames[1], text = "Next", command = next_question)
next_button.grid(column=2, row=5)
def next_question():
global frameNumber
frameNumber +=1
call_frame_frameNumber()
global frameNumber
frameNumber = 1
The problem is that python isn't recognizing "call_frame_frameNumber" as "call_frame_X" where X is changing each time the button is pressed. I get the error: NameError: name 'call_frame_frameNumber' is not defined
I know defining global variables isn't the best practice but it's all I really know at this point. If you can show a better way I'm more than willing to learn!
This isn't great coding practice as the amount of frames will increase but you could just use several 'if' statements to call on each function.
For example:
if frameNumber == 1:
call_frame_1()
After reviewing some comments by #PurpleIce and trying to further clean up my code, I found a solution to my problem.
I changed the code so that there wasn't a unique call_frame_X for each question but just one general call_frame with a variable of frameNumber as shown below.
def Next_Button():
next_button = Button(special_frames[1], text = "Next Question", command = next_question)
next_button.grid(column=0, columnspan=5, row=3)
def next_question():
global frameNumber
frameNumber +=1
call_frame(frameNumber)
Then, instead of having multiple create_widgets_in_X_frame, I created one general function that would iterate each time the "Next" button was selected.
def create_widgets_function(frameNumber):
current_frame=frames[frameNumber] #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
question_number = "question"+ str(frameNumber)
question_frame_populator(current_frame, question_number)
def call_frame(frameNumber):
all_frames_forget()
create_widgets_in_current_frame =create_widgets_function(frameNumber)
current_frame = frames[frameNumber]
check_remaining_lives(create_widgets_in_current_frame, current_frame)
This helped reduce/consolidate a lot of my code while simultaneously giving me a "Next" button.
Here is the updated code:
def Part1():
JSON_File = Root_File_Name + "Lessons\\Lesson_1\\"
with open(JSON_File+"Arabic_Lesson_1.json", "r", encoding = "utf-8-sig") as question_file:
data = json.load(question_file)
def create_widgets_in_first_frame(): # Create the label for the frame
current_frame=frames[0] #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
question_number = "question0"
question_frame_populator(current_frame, question_number)
def question_frame_populator(current_frame, question_number): #This is what displayes all of the information on the frame
questionDirectory = data["lesson 1"]["part one"][question_number] ##UPDATE PER QUESTION## This is the directory for the question.
wronganswer = questionDirectory["wronganswer"] #This is the directory for the wrong answers
question = questionDirectory.get("question") #This is the question text
correctanswer = questionDirectory.get("answer") #This is the answer for whichever question has been selected.
arabic = questionDirectory.get("arabic") #This is the arabic text for the question
transliteration = questionDirectory.get("transliteration")
global var
var = IntVar()
var.set(0) #Sets the initial radiobutton selection to nothing
answers = generate_answers(wronganswer, correctanswer) #Pulls answers generated from the "generate_answers" function
choices = []
for i in range(3):
choice = Radiobutton(current_frame, image=answers[i], variable = var, value=i+1, command= Check_Answer)
choice.image = answers[i]
choices.append(choice)
random.shuffle(choices) #This line of code randomizes the order of the radiobuttons.
choices[0].grid(row=1, column=0)
choices[1].grid(row=1, column=1)
choices[2].grid(row=1, column=2)
L1 = Label(current_frame, text=question, font=("Helvetica", 35)) #This displays the question at the top of the screen
L1.grid(columnspan=4, row=0)
transliteration_button = Button(current_frame, text="Show Transliteration", command= lambda: Transliteration(current_frame, arabic, transliteration)) # Makes the phonetic pronunciation button. #####
transliteration_button.grid(column=0, row=2)
#Previous_Button() # Creates the "previous" button and displays it.
Next_Button() # Creates the "next" button and displays it.
Quit_Button(current_frame) # Creates the "quit" button and displays it.
def Transliteration(current_frame, arabic, transliteration):
Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
Transliteration.grid(row=3, columnspan=4)
def generate_answers(wronganswer, correctanswer):
Wans=random.sample(wronganswer, 2)
images = [os.path.join(ImagePath, f"{Wans[i]}.png") for i in range(2)]
images += [os.path.join(ImagePath, f"{correctanswer}.png")]
answers = [PhotoImage(file=images[i]) for i in range(3)]
return answers
def Check_Answer():
global lives
global score
if str(var.get()) !="3":
special_frames[1].grid_forget() #This is the frame for right answers
special_frames[0].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for wrong answers
lives -=1
Incorrect = Label(special_frames[0], text ="That's incorrect!\n Lives: " +str(lives) + "\n Score: " + str(score), font=("Helvetica", 35))
Incorrect.grid(row=0, rowspan=2, column=0, columnspan=3)
if str(var.get()) == "3":
score +=1
special_frames[0].grid_forget() #This is the frame for wrong answers
special_frames[1].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for right answers
Correct = Label(special_frames[1], text = " That's right! \n Lives: " +str(lives)+ "\n Score: " + str(score), font=("Helvetica", 35))
Correct.grid(row=0, rowspan=2, column=0, columnspan=5)
def all_frames_forget():
for i in range(6): #This is for question frames
frames[i].grid_forget()
for i in range(3): #This is for special frames, like the correct and incorrect answer frames
special_frames[i].grid_forget()
def check_remaining_lives(create_widgets_in_current_frame, current_frame):
if lives<= 0:
Zero_Lives()
else:
create_widgets_in_current_frame
current_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
def Zero_Lives():
special_frames[2].grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
L5 = Label(special_frames[2], text="You have no remaining lives. \nPlease quit the lesson and try again.", font=("Helvetica", 35))
L5.grid(columnspan=4, row=0)
quit_button = Button(special_frames[2], text = "Quit", command = root_window.destroy)
quit_button.grid(column=1, columnspan = 2, row=2)
def Quit_Button(current_frame):
quit_button = Button(current_frame, text = "Quit", command = quit_program)
quit_button.grid(column=4, row=2)
def quit_program():
root_window.destroy()
def Next_Button():
next_button = Button(special_frames[1], text = "Next Question", command = next_question)
next_button.grid(column=0, columnspan=5, row=3)
def next_question():
global frameNumber
frameNumber +=1
call_frame(frameNumber)
def create_widgets_function(frameNumber):
current_frame=frames[frameNumber] #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
question_number = "question"+ str(frameNumber)
question_frame_populator(current_frame, question_number)
def call_frame(frameNumber):
all_frames_forget()
create_widgets_in_current_frame =create_widgets_function(frameNumber)
current_frame = frames[frameNumber]
check_remaining_lives(create_widgets_in_current_frame, current_frame)
def call_frame_1():
all_frames_forget()
create_widgets_in_current_frame = create_widgets_in_first_frame() #This line is unique
current_frame = frames[0] #This line is unique
check_remaining_lives(create_widgets_in_current_frame, current_frame)
##### Program starts here #####
Lesson1_FilePath = Root_File_Name + "Lessons\\Lesson_1\\"
ImagePath = Lesson1_FilePath + "Images\\"
root_window = Tk() # Create the root GUI window.
root_window.title("Lesson 1: Part 1") # Label the root GUI window.
global score
score = 0 #Setting the initial score to zero.
global lives
lives = 3 #Setting the initial number of lives.
global frameNumber
frameNumber = 1
window_width = 200 # Define window size
window_heigth = 100
frames = [] # This includes frames for all questions
for i in range(6):
frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
frame['borderwidth'] = 2
frame['relief'] = 'sunken'
frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
frames.append(frame)
special_frames=[] #This includes the frames for: wrong answers, right answers, and zero lives
for i in range(3):
special=tkinter.Frame(root_window, width=window_width, height=window_heigth)
special['borderwidth'] = 2
special['relief'] = 'sunken'
special.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
special.grid_forget()
special_frames.append(special)
call_frame_1() #Calls the first function which creates the firist frame
root_window.mainloop()
So I've been struggling with an issue for a week or so, been googling around trying to find different solutions, etc and getting nowhere. I was advised to put functioning code on here so I've cut it down some while still showing the issue.
I want to have a main page listing a set of goals, then if you click on the "Goal Entry" button up top a new window opens where you can input additional goals. Then you type in your desired additions, hit enter, and it adds it to the list on the main page.
I've accomplished all of the above EXCEPT, after you add the goals (and I have the list printing before and after so I know they're being added) and the entry window closes, the list of labels (created by an iteration) hasn't updated accordingly.
How do I get the list on the main page to automatically update when a new item is added to the list?
from tkinter import *
pg = ["goal1","goal2"]
pgtotal=1
psum=len(pg)
class yeargoals():
global pg, hg, fg, rg, rgtotal
def __init__(self,master):
self.master = master
master.title("This Year's Goals")
self.buttonframe = Frame(root)
self.buttonframe.pack(side=TOP, padx = 150, fill=BOTH)
self.home = Button(self.buttonframe, text="Home Page")
self.home.grid(row=1, column=1, padx=10)
self.enter = Button(self.buttonframe, text="Goal Entry", command=self.winenter)
self.enter.grid(row=1, column=2, padx=10)
self.finalize = Button(self.buttonframe, text="Finalize for Year")
self.finalize.grid(row=1, column=3, padx=10)
self.dashboard = Button(self.buttonframe, text="Goal Dashboard")
self.dashboard.grid(row=1,column=4, padx=10)
self.goalframe = Frame(root)
self.goalframe.pack(side=TOP, padx=150, pady=50, fill=BOTH, expand = True)
#Makes the label Fram I want the Checkboxes to go in
self.LabelFramep= LabelFrame(self.goalframe,text="Professional Goals")
self.LabelFramep.pack(side=LEFT, padx=10, anchor = N, fill=BOTH, expand = True)
#Makes the from the list above
for goal in pg:
l = Checkbutton(self.LabelFramep, text=goal, variable=Variable())
l.config(font=("Courier",12))
l.grid(sticky=W)
self.ptotal=Label(self.LabelFramep,text="Progress so far: "+str(pgtotal)+"/"+str(psum))
self.ptotal.config(font=("Courier",12))
self.ptotal.grid(sticky=W)
self.pper=Label(self.LabelFramep, text=str(round((pgtotal/psum)*100))+"% Complete")
self.pper.config(font=("Courier",12))
self.pper.grid(sticky=W)
def winenter(self):
global pg
self.winenter = Toplevel(root)
options = ["Professional", "Health", "Financial", "Reward Items"]
variable = StringVar(self.winenter)
variable.set(options[0])
#Title of entry section
t1 = Label(self.winenter, text="New Goal Entry")
t1.grid(row=0, column=1, columnspan=2)
#dropdown menu
d = OptionMenu(self.winenter, variable, *options)
d.grid(row=1, column=2)
#entry fields
e1 = Entry(self.winenter)
e1.grid(row=2, column=2, padx = 10, pady=5)
e2 = Entry(self.winenter)
e2.grid(row=3, column=2, padx=10, pady=5)
e3 = Entry(self.winenter)
e3.grid(row=4, column=2, padx=10, pady=5)
e4 = Entry(self.winenter)
e4.grid(row=5, column=2, padx=10, pady=5)
e5 = Entry(self.winenter)
e5.grid(row=6, column=2, padx=10, pady=5)
#Label for entry fields
l1 = Label(self.winenter, text="Goal Number 1")
l1.grid(row=2, column=1)
l2 = Label(self.winenter, text="Goal Number 2")
l2.grid(row=3, column=1)
l3 = Label(self.winenter, text="Goal Number 3")
l3.grid(row=4, column=1)
l4 = Label(self.winenter, text="Goal Number 4")
l4.grid(row=5, column=1)
l5 = Label(self.winenter, text="Goal Number 5")
l5.grid(row=6, column=1)
def enter():
global pg, main
print (pg)
if variable.get() == "Professional":
pg.append(e1.get())
self.winenter.destroy()
print (pg)
#Goal entry execute button
b = Button(self.winenter, text="Enter Goals", command=enter)
b.grid(row=7, column = 1)
root = Tk()
Window = yeargoals(root)
root.mainloop()
In your callback function to button "Enter Goals", you have done nothing to update your main window. Maybe you think the main window will magically keep updated with the variable pg, no, you need to do all those updates manually in your callback function.
For example, change your callback enter() to:
def enter():
global pg, main
print (pg)
if variable.get() == "Professional":
pg.append(e1.get())
l = Checkbutton(self.LabelFramep, text=pg[-1], variable=Variable())
l.config(font=("Courier",12))
l.grid(sticky=W)
self.winenter.destroy()
print (pg)
You can find the main window is updated after you click "Enter Goals".