Is there a way to not use global variables in my code? - python

I am relatively new to Python so please bear with the inefficiency of my code. This is one of my first Tkinter applications and it is quite a simple one. It is a code guessing game where the user tries to guess a randomly generated 4 digit code. The code currently prints the randomly generated number so that it is easier to see if the code is working. But is there a way to not use global variables in this code. Also, it would be helpful if I was told how to make my code efficient. Thanks in advance. Code:
import random
import tkinter as tk
number_of_tries = 0
def tries():
global number_of_tries
number_of_tries += 1
comment.configure(state="normal")
comment.delete(0, "end")
comment.insert(0, "Not quite! Keep trying!")
comment.configure(state="disabled")
if str(guess) == str(random_number):
comment.configure(state="normal")
comment.delete(0, "end")
comment.insert(0, "You guessed it in " + str(number_of_tries) + " tries!")
comment.configure(state="disabled")
guess_entry.configure(state="disabled")
go.configure(state="disabled")
def character_check():
global guess
guess = guess_entry.get()
if len(guess) != 4:
comment.configure(state="normal")
comment.delete(0, "end")
comment.insert(0, "Guess must be 4 digits! ")
comment.configure(state="disabled")
else:
for char in guess:
if char.isalpha():
comment.configure(state="normal")
comment.delete(0, "end")
comment.insert(0, "Guess must only include integers! ")
comment.configure(state="disabled")
else:
digit_check()
def digit_check():
global guess
guess = guess_entry.get()
for i in range(0, 4):
if str(guess)[i] == str(random_number)[i]:
digits[i] = str(guess)[i]
digit_display.configure(state="normal")
digit_display.delete(0, "end")
digit_display.insert(0, digits)
digit_display.configure(state="disabled")
elif str(guess)[i] != str(random_number)[i]:
digits[i] = "?"
digit_display.configure(state="normal")
digit_display.delete(0, "end")
digit_display.insert(0, digits)
digit_display.configure(state="disabled")
def initialise():
global number_of_tries, digits
global random_number
digits = ["?"] * 4
random_number = random.randrange(1000, 9999)
number_of_tries = 0
digit_display.configure(state="normal")
comment.configure(state="normal")
digit_display.delete(0, "end")
digit_display.insert(0, digits)
comment.delete(0, "end")
comment.insert(0, "Good luck!")
digit_display.configure(state="disabled")
comment.configure(state="disabled")
guess_entry.configure(state="normal")
go.configure(state="normal")
print(random_number)
screen = tk.Tk()
screen.geometry("400x400")
screen.title("Code Crunchers")
heading = tk.Label(text="Welcome to Code Crunchers!", font="Montserrat 15")
heading.place(relx=0.5, rely=0.1, anchor="center")
instruction_1 = tk.Label(text="Enter your guess below: ", font="Montserrat 10")
instruction_1.place(relx=0.5, rely=0.2, anchor="center")
guess_entry = tk.Entry(screen, width=4)
guess_entry.place(relx=0.5, rely=0.25, anchor="center")
go = tk.Button(text="Go", font="Montserrat 10", command=lambda: [character_check(), tries()])
go.place(relx=0.6, rely=0.27, anchor="center")
instruction_2 = tk.Label(text="Digits correct: ", font="Montserrat 10")
instruction_2.place(relx=0.5, rely=0.35, anchor="center")
digit_display = tk.Entry(screen, width=10)
digit_display.place(relx=0.5, rely=0.4, anchor="center")
comment = tk.Entry(screen, width=40)
comment.place(relx=0.5, rely=0.45, anchor="center")
restart = tk.Button(text="Restart", font="Montserrat 10", command=lambda: initialise())
restart.place(relx=0.5, rely=0.55, anchor="center")
initialise()
screen.mainloop()

What I would do is simply display the number of tries to the user and then read from that object to see the amount of tries. If you do not want to display it to the user you can always hide it so it does not display. If you do not want to have it on your GUI at all you should then pass this variable around as arguments in your functions.

Related

number guessing python tkinter validation

previous issue was resolved thanks to you guys can anyone also help me with the validation like if a person clicks check with empty field it prints a message in entry2 like "Field cannot be empty" .Adding code for reference .
from tkinter import *
from tkinter.messagebox import *
import random
root = Tk()
root.title('Number Guessing Game E-project')
root.geometry('500x500')
label1 = Label(root, text= "WELCOME TO NUMBER GUESSING GAME",fg='Maroon',font= ('bold',17))
label2 = Label(root, text= "Enter your guess between 0-999:",font=('Italics',15), fg='Maroon')
label1.grid(row=1 , column=0 ,ipadx=20,pady=10)
label2.grid(row=4 , column=0 ,ipadx=10,pady=10)
player1 = StringVar(root,value="Generate A Random Number First")
entry1 = Entry(root,textvariable=player1,width=30,bd=4)
entry2 = Entry(root,width=30,bd=4)
entry1.grid(row =12 , column= 0 ,ipadx=10,pady=10)
entry2.grid(row =13, column= 0 ,ipadx=10,pady=10)
attempts = 10
chances = StringVar()
chances.set("10 Attempts Remaining")
label4 = Label(root,textvariable=chances,font='Italics')
label4.grid(row=7,column=0 ,ipadx=10,pady=10)
def clear_the_screen():
entry1.delete(0,END)
def random_num_generate():
global random_number , user_guess
random_number = random.randint(0,999)
clear_the_screen()
def player_input():
try:
user_guess = int(entry1.get())
winning_logic(random_number,user_guess)
except ValueError:
entry2.insert(0,"Enter integers only!")
result3 = 'Enter integers only!'
entry2.delete(0, END)
entry2.insert(0, result3)
chances.set(f'{attempts} attempts remaining')
clear_the_screen()
def winning_logic(random_no,player_guess):
global attempts
global chances
attempts -= 1
if attempts < 1:
a = (f'You are out of attempts \n Try again')
showinfo("Sorry", a)
root.destroy()
elif player_guess == random_no:
result1 = 'Bravo! Success!'
entry2.delete(0, END)
entry2.insert(0,result1)
chances.set(f'You Win!')
win = (f'Congrats! You have guessed the correct number !')
showinfo(f'Number Guessing Game', win)
response = askquestion('Number Guessing Game', 'Did you like the game?')
if response == 'yes':
showinfo('Number Guessing Game', "That's Great!!")
else:
showinfo('Number Guessing Game',"So Sad!")
root.destroy()
elif player_guess < random_no and player_guess < 1000:
result2 = 'Guess Higher!'
entry2.delete(0,END)
entry2.insert(0, result2)
chances.set(f'Wrong! {attempts} attempts remaining')
clear_the_screen()
elif player_guess > random_no and player_guess < 1000:
result3 = 'Guess Lower!'
entry2.delete(0,END)
entry2.insert(0, result3)
chances.set(f'Wrong! {attempts} attempts remaining')
clear_the_screen()
elif player_guess > 999:
result3 = 'Invalid Number!'
entry2.delete(0, END)
entry2.insert(0, result3)
chances.set(f'Invalid Number! {attempts} attempts remaining')
clear_the_screen()
button1 = Button(root, text='Generate Random No',fg='white',bg='brown',command=random_num_generate)
button1.grid(row=9, column=0,ipadx=20,pady=8)
button2 = Button(root, text='Clear',fg='white',bg='brown',command=clear_the_screen)
button2.grid(row=18, column=0,ipadx=73,pady=10)
button3 = Button(root, text='Check',fg='white',bg='brown',command=player_input)
button3.grid(row=20, column=0,ipadx=70,pady=8)
root.mainloop()
You can check whether the input is empty in the except block inside player_input():
def player_input():
user_guess = entry1.get()
try:
user_guess = int(user_guess)
winning_logic(random_number, user_guess)
except ValueError:
if user_guess == '':
result3 = 'Field cannot be empty!'
else:
result3 = 'Enter integer only!'
entry2.delete(0, END)
entry2.insert(0, result3)
chances.set(f'{attempts} attempts remaining')
clear_the_screen()

TypeError: '>=' not supported between instances of 'str' and 'int'? [duplicate]

I am trying to make a guess game program where you have to guess a number between 0-100 on a GUI using Tkinter, and it also counts your amount of attempts but I get this error on line 25:
'<' is not supported between instances of str and int.
What can I do to solve this issue? This code would work on a command line but not when I have attempted to translate it into a GUI. I am also not sure if the code even works.
My updated code so far is:
import random
from tkinter import *
#need to count how many attempts you made
window = Tk()
window.title("Number guessing game")
window.geometry('350x200')
lbl = Label(window, text="Enter a number here from 1-100: ")
lbl.grid(column=0,row=0)
guess_var = IntVar(window)
txt = Entry(window, textvariable=guess_var)
txt= Entry(window, width=10)
txt.grid(column=1,row=0)
numguess = 0
secret_num = random.randint(1, 100)
def clicked():
if guess < 0:
lbl2.configure(text ="Please enter a sensible number.")
if guess > 100:
lbl2.configure(text ="Please enter a sensible number.")
if guess == secret_num:
lbl2.configure(text ="You guessed correctly.")
lbl3.confgure(text=numguess)
elif guess < secret_num:
numguess = numguess + 1
lbl2.configure(text ="higher!")
lbl = Label(window, text="Enter a number here from 1-100: ")
else:
numguess = numguess + 1
lbl2.configure(text ="lower!")
lbl = Label(window, text="Enter a number here form 1-100")
lbl2 = Label(window,text = " ")
lbl2.grid(column=1, row=2)
lbl3 = Label(window,text = " ")
lbl3.grid(column=1, row=3)
btn = Button(window, text="Enter", command=clicked)
guess = int(txt.get())
btn.grid(column=3, row=0)
window.mainloop()
guess = txt.get()
returns a string value. To be able to perform comparisons with integers, you need to convert guess from a string to an integer, i.e.:
guess = int(txt.get())
To be safe, you may want to handle the case where the user inputs something which cannot be converted to a integer; how to do this depends on the design of your problem - you should consider validating the user input.
One way to do this could be by using IntVar, e.g.:
guess_var = IntVar(window)
txt = Entry(window, textvariable=guess_var)
txt.grid(column=1,row=0)
numguess = 0
secret_num = random.randint(1, 100)
guess = guess_var.get()
Note that if you want to use the numguess global variable inside your clicked function, you need to declare global numguess in your function definition, e.g.:
def clicked():
global numguess

Guess the number with Tkinter, Python3

here is my code:
from tkinter import *
import random
def initGame():
window = Tk()
window.title("Guess the number game")
lbl = Label(window, text="Guess number from 1 to 100. Insert how many tries would you like to have: ", font=("",16))
lbl.grid(column=0, row=0)
txt = Entry(window, width=10)
txt.grid(column=0, row=1)
txt.focus() #place cursor auto
def clicked():
number_dirty = txt.get()
tries = int(number_dirty)
playGame(tries)
btn = Button(window, text="Start", command=clicked)
btn.grid(column=0, row=2)
window.geometry('800x600')
window.mainloop()
def playGame(tries):
number_of_tries = int(tries)
number = random.randint(1,100)
higher_notification = "Number is HIGHER"
lower_notification = "Number is LOWER"
game_window = Tk()
game_window.title("Game Window")
lbl = Label(game_window, text="Guess numbers between 1 and 100, you have %s tries !" %(number_of_tries), font=("",14))
lbl.grid(column=0, row=0)
txt = Entry(game_window, width=10)
txt.grid(column=0, row=1)
txt.focus()
print(number)
print(number_of_tries)
def clicked():
user_input = txt.get()
compareNumbers(number, user_input)
btn_try = Button(game_window, text="Try!", command="clicked")
btn_try.grid(column=0, row=2)
def compareNumbers(number, user_input):
if user_input == number:
messagebox.showinfo('You have won!', 'Right! the number was %s ' %(number))
else:
if user_input > number:
lbl.configure(lower_notification)
number_of_tries -1
else:
lbl.configure(higher_notification)
number_of_tries -1
game_window.geometry('600x600')
game_window.mainloop()
initGame()
On the first screen (initGame) everything works fine, when I click the button I do indeed get the second screen, which displays all objects normally. When I click the button in the game screen I get no feedback at all, nothing happens.
What am I missing?
Thank you very much !
The problem is in this line:
btn_try = Button(game_window, text="Try!", command="clicked")
Note that the command "clicked" is inside quotaions marks and therefor a string and not the method you tried to reference. What you want is:
btn_try = Button(game_window, text="Try!", command=clicked)

Keep track of score

I am trying to make a quiz that shows the user the name of the state and they have to correctly state the capital. Everything works fine, except for keeping track of the user's score. I have tried to change around the score portion of the code, but nothing is working! I think the problem is somewhere in the nextCapital() function, but then again I could be wrong. I am new to python and all of this is a little overwhelming. I would really appreciate the help!
import tkinter
import random
capitals={"Washington":"Olympia","Oregon":"Salem",\
"California":"Sacramento","Ohio":"Columbus",\
"Nebraska":"Lincoln","Colorado":"Denver",\
"Michigan":"Lansing","Massachusetts":"Boston",\
"Florida":"Tallahassee","Texas":"Austin",\
"Oklahoma":"Oklahoma City","Hawaii":"Honolulu",\
"Alaska":"Juneau","Utah":"Salt Lake City",\
"New Mexico":"Santa Fe","North Dakota":"Bismarck",\
"South Dakota":"Pierre","West Virginia":"Charleston",\
"Virginia":"Richmond","New Jersey":"Trenton",\
"Minnesota":"Saint Paul","Illinois":"Springfield",\
"Indiana":"Indianapolis","Kentucky":"Frankfort",\
"Tennessee":"Nashville","Georgia":"Atlanta",\
"Alabama":"Montgomery","Mississippi":"Jackson",\
"North Carolina":"Raleigh","South Carolina":"Columbia",\
"Maine":"Augusta","Vermont":"Montpelier",\
"New Hampshire":"Concord","Connecticut":"Hartford",\
"Rhode Island":"Providence","Wyoming":"Cheyenne",\
"Montana":"Helena","Kansas":"Topeka",\
"Iowa":"Des Moines","Pennsylvania":"Harrisburg",\
"Maryland":"Annapolis","Missouri":"Jefferson City",\
"Arizona":"Phoenix","Nevada":"Carson City",\
"New York":"Albany","Wisconsin":"Madison",\
"Delaware":"Dover","Idaho":"Boise",\
"Arkansas":"Little Rock","Louisiana":"Baton Rouge"}
score=0
timeleft=30
print("This program will launch a capital quiz game.")
input1 = input("What difficulty would you like to play: easy, normal, or hard?\n")
if input1.lower() == "easy":
seconds = 90
timeleft = seconds
elif input1.lower() == "normal":
seconds = 60
timeleft = seconds
elif input1.lower() == "hard":
seconds = 30
timeleft = seconds
def startGame(event):
#if there's still time left...
if timeleft == seconds:
#start the countdown timer.
countdown()
#run the function to choose the next colour.
nextCapital()
if timeleft == 0:
endlabel = tkinter.Label(root, text="The time is up!\nYour score is: " + str(score) +" out of 50", font=('Helvetica', 12))
endlabel.pack()
e.pack_forget()
#function to choose and display the next colour.
def nextCapital():
#use the globally declared 'score' and 'play' variables above.
global score
global timeleft
#if a game is currently in play...
if timeleft > 0:
#...make the text entry box active.
e.focus_set()
randchoice = random.choice(list(capitals.keys()))
answer = capitals.get(randchoice)
if answer.lower() == randchoice.lower():
score = score+1
#### #this deletes the random choice from the dictionary
#### del capitals[randchoice]
#clear the text entry box.
e.delete(0, tkinter.END)
#this updates the random choice label
label.config(text=str(randchoice))
#update the score.
scoreLabel.config(text="Score: " + str(score))
#a countdown timer function.
def countdown():
#use the globally declared 'play' variable above.
global timeleft
#if a game is in play...
if timeleft > 0:
#decrement the timer.
timeleft -= 1
#update the time left label.
timeLabel.config(text="Time left: " + str(timeleft))
#run the function again after 1 second.
timeLabel.after(1000, countdown)
#create a GUI window.
root = tkinter.Tk()
#set the title.
root.title("Capital Quiz")
#set the size.
root.geometry("500x250")
#add an instructions label.
instructions = tkinter.Label(root, text="Brush up your geography skills!", font=('Helvetica', 12))
instructions.pack()
#add a score label.
scoreLabel = tkinter.Label(root, text="Press enter to start" + str(score), font=('Helvetica', 12))
scoreLabel.pack()
#add a time left label.
timeLabel = tkinter.Label(root, text="Time left: " + str(timeleft), font=('Helvetica', 12))
timeLabel.pack()
#prompt label
promptLabel = tkinter.Label(root, text= "Enter the capital of: ", font=('Helvetica', 12))
promptLabel.pack()
#add a label that will hold print the prompt
label = tkinter.Label(root, font=('Helvetica', 60))
label.pack()
#add a text entry box for typing in colours.
e = tkinter.Entry(root)
#run the 'startGame' function when the enter key is pressed.
root.bind('<Return>', startGame)
e.pack()
#set focus on the entry box.
e.focus_set()
#start the GUI
root.mainloop()
randchoice is one of the keys in the capitals dict, i.e. a State.
answer is one of the values in the capitals dict, i.e. a Capital
You then compare the lowercase versions of randchoice and answer and increment the score if they are equal. But clearly they will never be equal (one is a State, one is a Capital). So your score won't be updated properly.
Not an answer, but a bit of advice: eschew all global variables, wrap things into an object with clearly defined, short methods. You will have much easier time working with and reasoning about the code.
Consider this skeleton of a class:
class Quiz(object):
def __init__(self, difficulty, capitals_dict):
self.score = 0
self.capitals = capitals
self.difficulty = ...
self.right_answer = None
def getNextQuestion(self):
# Choose a capital and assign it as the current right answer
...
self.right_answer = ...
return self.right_answer # to show it to user
def checkAnswer(user_answer):
if user_answer == self.right_answer:
self.score = ...
return True
else:
...
def isGameOver(self):
return len(self.capitals) == 0
I guess it's clear enough how to use a class like this, and reasonably clear how to implement it.

tkinter: How to change labels inside the program itself

I was asked to edit my code so I decided to include the entire calculator script
from tkinter import *
global choice
choice = 0
#Program
def calculate(*event):
if choice == 1:
try:
add1 = ccalc1.get()
add2 = ccalc2.get()
except:
no = Label(app, text="You must use a number").grid(row=0, column=0)
answ = add1 + add2
answer = Label(app, text = answ).grid(row=1, column=0)
elif choice == 2:
try:
sub1 = ccalc1.get()
sub2 = ccalc2.get()
except:
no = Label(app, text="You must use a number").grid(row=1, column=0)
answ = sub1 - sub2
answer = Label(app, text = answ).grid(row=1, column=0)
def choice2():
global choice
choice = 2
#End Program
#GUI
#Window Info
calc = Tk()
calc.title("Calculator")
calc.geometry("200x140")
#End Window Info
#Build Window
app = Frame(calc)
app.grid()
ccalc1 = IntVar()
ccalc2 = IntVar()
#Widgets
if choice == 0:
welcome = Label(app, text="Select a choice")
elif choice == 2:
welcome.config(text="Subtraction")
calcbox1 = Entry(app,textvariable=ccalc1)
calcbox2 = Entry(app,textvariable=ccalc2)
submit = Button(app, text="CALCULATE", command = calculate)
welcome.grid(row=0,column=0)
calcbox1.grid(row=2, column=0)
calcbox2.grid(row=3, column=0)
submit.grid(row=4, column=0)
calc.bind('<Return>', calculate)
#End Widgets
#Menu
menu=Menu(calc)
#Operations
filemenu = Menu(menu,tearoff=0)
filemenu.add_command(label="Subtract", command = choice2)
menu.add_cascade(label="Operations",menu=filemenu)
calc.config(menu=menu)
calc.mainloop()
#End GUI
what wrong is that the welcome label text wont change accordingly.
Update: I included the entire calculator code
Any help is appreciated.
It's hard to understand what you expect to happen. For example, look at this code:
#Widgets
if choice == 0:
welcome = Label(app, text="Select a choice")
elif choice == 2:
welcome.config(text="Subtraction")
This code will only ever execute once, and choice will always be zero since that's what you initialize it to. It executes once because it's not in a function and not in a loop, so as python parses the code it will run it and move to the next line. That block of text will never be processed a second time.
If you want the label to change when the user selects a menu item, you'll need to execute that code inside the choice2 function:
def choice2():
global choice
choice = 2
welcome.config(text="Subtraction")

Categories