Because I've used OOP all frames are initialised and run functions even without populating the set container. This means that if my game is running it will continue to run even when the window is changed. I would like to make it so that if a new frame is shown all functions/methods stop running for other windows. Currently, if the game is started the countdown will continue to go regardless of what's being shown (eg the menu screen).
Here is the class that all the windows inherit from and are sorted:
class TextTypers(tk.Tk):
def __init__(self, *args, **kwargs): # Runs when our class is called and allows almost anything to be passed
tk.Tk.__init__(self, *args, **kwargs) # Initialise Tk
window = tk.Frame(self) # Creates the container the windows/frames will populate
window.pack(fill="both", expand=True)
self.frames = {} # Creates a dictionary for the frames
for F in (MenuScreen, GameScreen, InstructionsScreen):
frame = F(window, self) # Adds all frames to dictionary so they can be accessed later
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nswe")
self.show_frame(MenuScreen) # Shows the menu screen as this is initialised when the program is run
def show_frame(self, cont):
frame = self.frames[cont] # Grabs value of self.frames and puts in in frame
frame.tkraise() # Raises frame to the front
Here is the relevant GUI for the game screen:
class GameScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent) # Inherits from main class
self.label = tk.ttk.Label(self, text="Type the words as quick as you can!",
font=("arial", 35, "bold"), justify="center")
self.label.grid(row=1, column=3, pady=10, padx=10)
self.time_label = tk.ttk.Label(self, text="Time left: " + str(timer), font=("arial", 30, "bold"),
justify="center")
self.time_label.grid(row=3, column=3, columnspan=2, padx=10, pady=10)
self.word_label = tk.ttk.Label(self, font=("arial", 25, "bold"), justify="center")
self.word_label.grid(row=5, column=3, padx=10, pady=10)
self.entry = tk.DoubleVar()
self.entry.set("")
self.entry = tk.ttk.Entry(self, textvariable=self.entry, font=("arial", 25, "bold"), state='disabled')
self.entry.grid(row=6, column=3)
self.start_button = tk.Button(self, text="Start!", font=("arial", 20, "bold"),
command=self.game, width=10, height=2)
self.start_button.grid(row=7, column=3, pady=10, padx=10)
self.entry.bind('<Return>', self.check_word)
self.home_button = tk.Button(self, text="Home", font=("arial", 20, "bold"),
command=lambda: controller.show_frame(MenuScreen), width=10, height=2)
self.home_button.grid(row=8, column=3, pady=10, padx=10)
def game(self):
global timer
timer = 60
self.entry.config(state="enabled")
self.entry.delete("0", "end")
self.start_button.config(text="Stop", command=lambda: self.stop())
if timer == 60: # Starts the timer and calls the words function
self.countdown()
self.words()
def countdown(self):
global timer
if timer > 0:
timer -= 1
# Update the time left label
self.time_label.config(text="Time left: " + str(timer))
# Run the function again after 1 second
self.time_label.after(1000, self.countdown)
def words(self):
global timer
global word
if timer > 0:
self.entry.focus_set() # Activate the entry box
word = random.choice(word_list[0])
self.word_label.config(text=word)
if timer == 0:
self.results(score)
def check_word(self, event):
global word, score, wrong
entered_word = self.entry.get().lower().strip()
if entered_word == word.lower().strip():
score += 1
else:
wrong += 1
self.words()
self.entry.delete('0', 'end')
def results(self, score):
global top_score
self.entry.config(state='disabled')
if score > top_score:
top_score = score
else:
top_score = top_score
accuracy = score / (score + wrong) * 100
self.word_label.config(
text=f"In one minute you correctly typed {score} words.\n"
f"This is a WPM of {score}, your actual WPM is most likely higher.\n "
f"You got {score} words correct and {wrong} words wrong. \n"
f" That gives you an accuracy of {accuracy:.1f}%!\n"
f"Your top score is {top_score}!", font=("arial", 20, "bold"))
self.start_button = tk.Button(self, text="Play again?", font=("arial", 20, "bold"), command=self.game)
self.start_button.grid(row=7, column=3, pady=10, padx=10)
return top_score