I've been developing my first real python gui-program for a while now. Having watched some tutorials to tkinter and OOP I've gotten quite far, but now I just can't get my code to do what I want it to.
So here's my code so far:
import random
import string
import time
import datetime
import tkinter as tk
import pandas as pd
# GUI
# Define text variabels:
intro_txt = "Welcome to Typetrainer!\n This program will help you get faster at typing on your PC"
instr_txt = "Instructions:\n-Place your right index finger on the letter J, your left index finger on F" \
"\n-Type the string you see as fast as you can using all ten fingers" \
"\n-Don't look at the keyboard !"
diff_txt = "Please select a difficulty"
go_txt = "Type the above string as fast as you can and hit enter !"
# Create custom fontvariables
NORMAL_FONT=("Verdana", 12)
LARGE_FONT=("Verdana", 18)
# Define difficulties
diff_levels = {"Easy": 5, "Medium": 10, "Hard": 15, "Expert": 20}
class TypeTrainerGUI(tk.Tk):
def __init__(self, *args, **kwargs): # initialize the TypeTrainerGUI class;
# *args: generally used for all the arguments, **kwargs: passing through dictionaries
tk.Tk.__init__(self, *args, **kwargs) # initialize Tkinter within the TypeTrainerGUI class
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {} # this holds all the different "windows" that the GUI can show and the user can interact with
for F in (StartPage, DifficultySelection, TypePage): # "activates" the frames so that
# they can be cycled through
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew") # .grid defines where the frame sits; sticky defines
# alignment and strech
self.title("Type Trainer")
self.show_frame(StartPage)
def show_frame(self, cont): # define the .show_page method; moves a frame to the top so
# the user can interact with it
frame = self.frames[cont]
frame.tkraise()
def init_game(self, diff_in): # set the parameters for the game to run with
global cnt
global score
print(diff_in) # check if the correct value is passed
# Define a object that creates a lowercase letter string of length a
self.question_txt = ''.join(random.choice(string.ascii_lowercase) for x in range(diff_in))
print(self.question_txt) # check if correct text is generated
cnt = 0 # Counter for the no of rounds
score = 0 # Track the score
class StartPage(tk.Frame): # Create start page
def __init__(self, parent, controller): # initialize the frame
tk.Frame.__init__(self, parent) # initialize tkinter
label = tk.Label(self, text=intro_txt, font=LARGE_FONT) # create label
label_2 = tk.Label(self, text=instr_txt, font=NORMAL_FONT) # create label
label.pack(pady=10, padx=10)
label_2.pack(pady=10, padx=10)
next = tk.Button(self, text="Next", command=lambda: controller.show_frame(DifficultySelection))
next.pack()
class DifficultySelection(tk.Frame): # Create difficulty selection page
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text=diff_txt, font=NORMAL_FONT, justify=tk.LEFT)
label.pack(pady=10, padx=10)
self.choice = tk.IntVar()
for (diff, val) in diff_levels.items():
tk.Radiobutton(self, text=diff, padx=20, variable=self.choice, value=val,
command=lambda: controller.init_game(self.choice.get())).pack(anchor=tk.W)
start = tk.Button(self, text="Start game", command=lambda: controller.show_frame(TypePage))
start.pack()
class TypePage(tk.Frame): # Create the interaction page
def __init__(self, parent, init_game):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text=go_txt, font=NORMAL_FONT)
label.pack(pady=10, padx=10)
question = tk.Label(self, text=question_txt, font=NORMAL_FONT)
question.pack(pady=10, padx=10)
usr_input = tk.Entry(self)
usr_input.pack(pady=10, padx=10)
usr_input.focus_set()
gui = TypeTrainerGUI()
gui.mainloop()
Ok, so what I want to do is get the value that's selected by clicking a radiobutton and pass it to init_game
to create question_txt
(and other stuff but I'm leaving that out for now). To do this I use this part of code
self.choice = tk.IntVar()
for (diff, val) in diff_levels.items():
tk.Radiobutton(self, text=diff, padx=20, variable=self.choice, value=val,
command=lambda: controller.init_game(self.choice.get())).pack(anchor=tk.W)
This all works just fine. init_game
does what I want it to do (checked with print(diff_in)
and print(question_txt)
). Now I want to use question_txt
in the TypePage
class as text for the question label.
question = tk.Label(self, text=question_txt, font=NORMAL_FONT)
question.pack(pady=10, padx=10)
Doing this throws a name error: "name 'question_txt' is not defined". I just don't get how I can use question_txt
in a different class. I tried to make it a global variable, but that also didn't solve the problem.
I would really appreciate any help with this. Thanks !
N.B.: Since this is my first try at OOP AND tkinter please be leneant. Though all comments on how to write better/more understanbale code are greatly appreciated !