I am in the process of making a Hangman type game. So far, I have written the CLI version which works well, i'm just porting it over to create a GUI.
I have become stuck :-(. The program is not complete, and there's still more to do but I have two issues. The first is the label update.
When a letter is chosen, it creates enough dashes for the letter, and places this in a label called 'letter'.
When a user enters a letter, it replaces the dashes, however it then adds a new label next to the old label, instead, I would like to replace this label. I have tried using the .set() but this doesn't seem to work.
My second issue, and this is more of a logic error (I think), is that I wanted to keep track of the letters entered so that I could compare this to newly entered letters and alert the user. This works well, however when a letter has been entered it will warn the user even if its the first time it has been typed.
Here's the code:
import tkinter
from tkinter import *
from tkinter import messagebox
import random
guesses = 8
def play():
print("play game")
wordList = ["talking", "dollar","choice", "famous", "define", "features"]
wordChoice = random.choice(wordList)
print(wordChoice)
wordLength = (len(wordChoice))
print(wordLength)
guessedLetters = []
dashes = []
def enterLetter():
print("Guess")
global guesses
print(guessedLetters)
while guesses != 0:
guess = entry.get().lower()
if len(guess) >1:
messagebox.showinfo("Error","Sorry, only one letter at a time")
entry.delete("0","end")
return
elif guess.isalpha() == False:
messagebox.showinfo("Error","Letters only please")
entry.delete("0","end")
return
elif guess in guessedLetters:
messagebox.showinfo("Error","You have already used the letter")
entry.delete("0","end")
return
guessedLetters.append(guess)
print(guessedLetters)
print(guesses)
count = 0
for i in range(wordLength):
if wordChoice[i] == guess:
dashes[i] = guess
count = count +1
letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10)
if count == 0:
guesses -= 1
if guesses == 0:
print("You have ran out of guesses!")
print("The word was:",wordChoice)
###### Play Game GUI
play = Toplevel()
play.title("Play Hangman!")
label = Label(play, text="HANGMAN", font = ("Arial",16)).grid(row = 0)
label = Label(play, text="Enter your guess >").grid(row = 3, column = 0)
for i in range(wordLength):
dashes.append("-")
letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10)
entry = Entry(play)
entry.grid(row = 3, column = 1, columnspan = wordLength)
enterButton = Button(play, text = "Enter Guess", width = 15, command = enterLetter).grid(row = 3, column = (wordLength+2))
label = Label(play, text = "Letter used: ").grid(row = 4, columnspan = 2)
label = Label(play, text = "").grid(row= 4, columnspan = 6)
def scores():
print("check scores")
def howToPlay():
print("how to play")
####### Main Menu
root = Tk()
root.geometry("500x300")
root.title("HANGMAN")
label = Label(root, text="HANGMAN", font = ("Arial",30)).grid(row = 0, columnspan = 3)
label = Label(root, text = "Option 1 :", font = ("Arial",12)).grid(row = 1, column = 1)
playButton = Button(root, text = "Play Game", width = 15, command = play).grid(row = 1, column = 2)
label = Label(root, text = "Option 2 :", font = ("Arial",12)).grid(row = 2, column = 1)
instructionsButton = Button(root, text = "How to play", width = 15, command = howToPlay).grid(row = 2, column = 2)
label = Label(root, text = "Option 3 :", font = ("Arial",12)).grid(row = 3, column = 1)
scoresButton = Button(root, text = "View Scores", width = 15, command = scores).grid(row = 3, column = 2)
label = Label(root, text = "Option 4 :", font = ("Arial",12)).grid(row = 4, column = 1)
exitButton = Button(root, text = "Exit", width = 15, command = exit).grid(row = 4, column = 2)
root.mainloop()
You need to configure the Label, not recreate it.
Why do you use a while-loop in enter_letter? Its just run when the Button is clicked, it needs to be an if guesses > 0:
Your program did not terminate when the right word was entered; I added this.
Code:
import tkinter
from tkinter import *
from tkinter import messagebox
import random
guesses = 8
letter = None
def play():
global letter
print("play game")
wordList = ["talking", "dollar","choice", "famous", "define", "features"]
wordChoice = random.choice(wordList)
print(wordChoice)
wordLength = (len(wordChoice))
print(wordLength)
guessedLetters = []
dashes = []
play = Toplevel()
play.title("Play Hangman!")
label = Label(play, text="HANGMAN", font = ("Arial",16)).grid(row = 0)
label = Label(play, text="Enter your guess >").grid(row = 3, column = 0)
for i in range(wordLength):
dashes.append("-")
letter = Label(play, text = dashes, font = ("Arial",20))
letter.grid(row = 2, column = i+1,padx = 10, pady =10)
print(letter)
def enterLetter():
print("Guess")
global guesses, letter
print(guessedLetters)
if guesses != 0:
guess = entry.get().lower()
if len(guess) >1:
messagebox.showinfo("Error","Sorry, only one letter at a time")
return
elif guess.isalpha() == False:
messagebox.showinfo("Error","Letters only please")
return
elif guess in guessedLetters:
messagebox.showinfo("Error","You have already used the letter")
return
entry.delete("0","end")
guessedLetters.append(guess)
#print(guessedLetters)
#print(guesses)
print(dashes)
count = 0
for i in range(wordLength):
if wordChoice[i] == guess:
dashes[i] = guess
count += 1
letter.configure(text = dashes)
if count == 0:
guesses -= 1
if "".join(dashes) == wordChoice:
print("succsess!")
play.destroy()
return
if guesses == 0:
print("You have ran out of guesses!")
print("The word was:",wordChoice)
###### Play Game GUI
entry = Entry(play)
entry.grid(row = 3, column = 1, columnspan = wordLength)
enterButton = Button(play, text = "Enter Guess", width = 15, command = enterLetter).grid(row = 3, column = (wordLength+2))
label = Label(play, text = "Letter used: ").grid(row = 4, columnspan = 2)
label = Label(play, text = "").grid(row= 4, columnspan = 6)
def scores():
print("check scores")
def howToPlay():
print("how to play")
####### Main Menu
root = Tk()
root.geometry("500x300")
root.title("HANGMAN")
label = Label(root, text="HANGMAN", font = ("Arial",30)).grid(row = 0, columnspan = 3)
label = Label(root, text = "Option 1 :", font = ("Arial",12)).grid(row = 1, column = 1)
playButton = Button(root, text = "Play Game", width = 15, command = play).grid(row = 1, column = 2)
label = Label(root, text = "Option 2 :", font = ("Arial",12)).grid(row = 2, column = 1)
instructionsButton = Button(root, text = "How to play", width = 15, command = howToPlay).grid(row = 2, column = 2)
label = Label(root, text = "Option 3 :", font = ("Arial",12)).grid(row = 3, column = 1)
scoresButton = Button(root, text = "View Scores", width = 15, command = scores).grid(row = 3, column = 2)
label = Label(root, text = "Option 4 :", font = ("Arial",12)).grid(row = 4, column = 1)
exitButton = Button(root, text = "Exit", width = 15, command = exit).grid(row = 4, column = 2)
root.mainloop()
I hope this helps you.
The reason you cant update your label is because you haven't stored it in any variable. The grid, pack and place functions of the Label object and of all other widgets returns None, therefore when you call:
letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10)
your label cannot be accessed by variable letter. To fix this you should split it like so:
letter = Label(play, text = dashes, font = ("Arial",20))
letter.grid(row = 2, column = i+1,padx = 10, pady =10)
To update text of that label, you can call .configure(text = 'new text') on it.
# letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10) #
letter.configure(text = dashes)
As for your second issue, i think you confused while loop and if statement in the function enterLetter. It's called once per click and you only need to check one time if player has ran out of guesses.
if guesses != 0:
...
elif guesses == 0:
....
Related
I have a tkinter listbox inside a frame which is inside a canvas. The scrollbar works very well through this code:
messagesList.config(yscrollcommand = scrollbar.set)
scrollbar.config(command=messagesList.yview)
However, I wanted the scrollbar to be longer so that I can actually scroll. I tried things such as making the frame bigger or make it take more space with padx and pady. Is there any way to make the scrollbar longer?
The image of how it currently is:
Complete code:
import tkinter as tk
from tkinter import ttk
import tkinter.font as tkFont
root = tk.Tk()
root.title("Messager")
root.geometry("1300x700")
root.resizable(height = 0, width = 0)
#makes window not resizable
class Elements:
def __init__(self, main):
def sendMessage():
user = userName.get()
message = userMessage.get()
print(user+ " username")
print(message + " message")
theCan = tk.Canvas(main)
titleFrame = tk.LabelFrame(main)
mainFrame = tk.LabelFrame(theCan)
greeting = tk.Label(titleFrame, text="Messager", bg = "#74a5f2", font = (None, 35))
userName = tk.Entry(titleFrame, font = (None, 15))
userName.insert(0, "An Unnamed Scrub")
userMessage = tk.Entry(titleFrame, font = (None, 15))
userMessage.insert(0,"Your Message")
sendButton = tk.Button(titleFrame, command = sendMessage, text = "Send!", bg = "#74a5f2", font = (None, 22))
titleMessage = tk.Label(mainFrame, text = "MESSAGER", bg = "#74a5f2", font = (None, 50))
messagesList = tk.Listbox(mainFrame)
scrollbar = tk.Scrollbar(mainFrame, orient=tk.VERTICAL, relief = 'flat')
messagesList.config(yscrollcommand = scrollbar.set)
scrollbar.config(command=messagesList.yview)
testList = ["apple", "orange","apple", "orange","apple", "orange","apple", "orange","apple", "orange","apple", "orange","apple", "orange"]
for item in testList:
messagesList.insert(tk.END, item)
placeholder = tk.Label(main, text = " ")
placeholder1 = tk.Label(main, text = " ")
placeholder2 = tk.Label(main, text = " ")
placeholder3 = tk.Label(main, text = " ")
placeholder4 = tk.Label(main, text = " ")
placeholder5 = tk.Label(main, text = " ")
placeholder6 = tk.Label(main, text = " ")
placeholder7 = tk.Label(main, text = " ")
placeholder8 = tk.Label(main, text = " ")
placeholder9 = tk.Label(main, text = " ")
placeholder10 = tk.Label(main, text = " ")
placeholder11 = tk.Label(main, text = " ")
placeholder12 = tk.Label(main, text = " ")
placeholder13 = tk.Label(main, text = " ")
placeholder.grid(row = 1, column = 1)
placeholder1.grid(row = 2, column = 2)
placeholder2.grid(row = 3, column = 3)
placeholder3.grid(row = 4, column = 4)
placeholder4.grid(row = 5, column = 5)
placeholder5.grid(row = 6, column = 6)
placeholder6.grid(row = 7, column = 7)
placeholder7.grid(row = 8, column = 8)
placeholder8.grid(row = 1, column = 9)
placeholder9.grid(row = 1, column = 10)
placeholder10.grid(row = 1, column = 11)
placeholder11.grid(row = 1, column = 12)
placeholder12.grid(row = 1, column = 13)
placeholder13.grid(row = 1, column = 14)
#placeholders to move the mainframe frame to the center
titleFrame.grid(row = 1, padx = 20, pady = 20)
greeting.grid(pady = 20)
userName.grid()
userMessage.grid(pady = 20)
sendButton.grid()
mainFrame.grid()
titleMessage.grid(pady = 20)
messagesList.grid()
theCan.grid(row = 1, column = 15, pady = 20)
scrollbar.grid(sticky = "ne", rowspan = 5)
postEverything = Elements(root)
root.mainloop()
As has been said already, you need to place the box and the scroll bar on the same row using grid
messagesList = tk.Listbox(mainFrame)
scrollbar = tk.Scrollbar(mainFrame, orient=tk.VERTICAL, relief = 'flat')
messagesList.config(yscrollcommand = scrollbar.set)
scrollbar.config(command=messagesList.yview)
messagesList.grid(row=0,column=0)
scrollbar.grid(row=0,column=1,sticky = "ns")
Also note that I've changed the sticky attribute to be "ns" which means the scroll bar will stretch to fit the row and match the height of the messagesList widget.
I was trying to make a calculator using Tkinter in python 3.8 and I managed to do the Buttons and the label and to make Addition using 2 numbers
the problem is I want to make it continue adding if I add more numbers But I couldn't do it.
here is a simple version of my code.
code :
import tkinter as tk
first = None
method = None
second = None
result = None
def OnButtonClick(button_id):
global first
global second
global method
global result
if first == None :
first = button_id
elif first != None and button_id == "+":
method = button_id
elif first != None and second == None and method != None:
second = button_id
elif first != None and second != None and button_id == "=":
result = first + second
print(result)
window = tk.Tk()
window.title('Calculator')
window.geometry("307x500")
button = window.title('Calculator')
window.geometry("307x500")
B1 = tk.Button(window, text = "1", width = 2, height = 2, command=lambda: OnButtonClick(1) ).grid(row = 7, column = 0)
B2 = tk.Button(window, text = "2", width = 2, height = 2, command=lambda: OnButtonClick(2)).grid(row = 7, column = 1)
B3 = tk.Button(window, text = "3", width = 2, height = 2, command=lambda: OnButtonClick(3)).grid(row = 7, column = 2)
BPlus = tk.Button(window, text = "+", width = 2, height = 2, command=lambda: OnButtonClick("+")).grid(row = 7, column = 3)
BEqual = tk.Button(window, text = "=", width = 2, height = 2, command=lambda: OnButtonClick("=")).grid(row = 8, column = 3)
window.mainloop()
The way you are doing it is really messy, no offence. In this way, you would need to use a lot of base cases, which might take a long time to code.
A simpler way to do this would be to use the eval(). If the button_id is the equal button, then we can just use eval() to calculate the calculation because we are appending the other button_id's to the string op.
Here is the code:
import tkinter as tk
first = None
method = None
second = None
result = None
op = ""
def OnButtonClick(button_id):
global first, second, method, result, op
if button_id == "=":
print(eval(op))
else:
op += str(button_id)
window = tk.Tk()
window.title('Calculator')
window.geometry("307x500")
button = window.title('Calculator')
window.geometry("307x500")
B1 = tk.Button(window, text = "1", width = 2, height = 2, command=lambda: OnButtonClick(1) ).grid(row = 7, column = 0)
B2 = tk.Button(window, text = "2", width = 2, height = 2, command=lambda: OnButtonClick(2)).grid(row = 7, column = 1)
B3 = tk.Button(window, text = "3", width = 2, height = 2, command=lambda: OnButtonClick(3)).grid(row = 7, column = 2)
BPlus = tk.Button(window, text = "+", width = 2, height = 2, command=lambda: OnButtonClick("+")).grid(row = 7, column = 3)
BEqual = tk.Button(window, text = "=", width = 2, height = 2, command=lambda: OnButtonClick("=")).grid(row = 8, column = 3)
window.mainloop()
Hope this helps!
I'm new in Python, I am searching for solution with this error. I got stuck with this assignment.
I'm trying to change the description by each picture but unfortunately I failed.
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
root.title("Image Viewer")
root.config(bg = "Grey")
frame1 = Frame(root, width = 500, height = 325, bg = "Silver")
frame1.pack(side = TOP)
frame2 = Frame(root, width = 500, height = 25, borderwidth= 1, bg = "Grey")
frame2.pack(side = BOTTOM, pady= 2)
# Image:
img1 = ImageTk.PhotoImage(Image.open("dec19.jpg"))
img2 = ImageTk.PhotoImage(Image.open("dec20.jpg"))
img3 = ImageTk.PhotoImage(Image.open("dec21.jpg"))
# Description:
des1= Label(frame1, text = "I am happy this day")
des2= Label(frame1, text = "going somewhere")
des3= Label(frame1, text = "Today is a great day")
# , width = 500, height = 315
num = 1
# List:
img_list = [img1, img2,img3]
des_list = [1 , 2, 3]
# Startup:
my_label = Label(frame1,image = img1)
my_label.pack()
my_des = Label(frame1, text = f"Description{num}")
my_des.pack(side = BOTTOM)
# Definning Command functions:
def close_app():
exit()
def forward(image_num):
global my_label
global prev
global next1
global num
global my_des
global des_list
my_label.pack_forget()
# .grid_forget()
my_label = Label(frame1, image= img_list[image_num-1])
my_label.pack()
my_des.pack_forget()
my_des = Label(frame1, text = f"Description{des_list[image_num-1]}")
my_des.pack()
# .grid(row = 0, column = 0, columnspan = 3)
next1 = Button(frame2, text = "Next", command = lambda: forward(image_num+1))
next1.grid(row = 1, column = 2)
prev = Button(frame2, text = "Previous", command = lambda : back(image_num-1))
prev.grid(row = 1, column = 0)
if image_num == 3:
next1 = Button(frame2, text = "Next", state = DISABLED)
next1.grid(row = 1, column = 2)
prev = Button(frame2, text = "Previous", state = DISABLED, command =lambda : back(2))
prev.grid(row = 1, column = 0)
exits = Button(frame2, text = "Exit", command = close_app)
exits.grid(row = 1, column = 1)
next1 = Button(frame2, text = "Next", command = lambda: forward(2))
next1.grid(row = 1, column = 2)
root.mainloop()
If you want that each description is different you can create a dictionary wher you bind each image index to its description
image_description = {
1 : 'I am happy this day',
2 : "going somewhere",
3: "Today is a great day"
}
Now that you have this dictionary when you call the function that changes the image instead having a static string that changes only the number ( f"Description{des_list[image_num-1]}" ) you can get the datas from the dictionary
so change this 3 lines
my_des.pack_forget()
my_des = Label(frame1, text = f"Description{des_list[image_num-1]}")
my_des.pack()
to this
my_des.configure(text = f"{image_description[image_num]}")
I'm writing a program that shows the Fibonacci sequence one number at a time. You are supposed to put in the number of numbers you want, and the numbers show one at a time. However, when I press the button, it just shows 1.
The first version of the code didn't work, because you would press the button and just get the final number. I tried (and failed) to fix it by adding "return fib" to the function, but that caused the current problem.
from time import sleep
from tkinter import *
def calc():
a = 1
b = 0
c = 0
w = texxt.get()
num = int(w)
for i in range(num):
if a >= b:
fib.set(str(a))
elif b >= a:
fib.set(str(b))
else:
fib.set("ERROR")
break
c = a + b
if a >= b:
b = c
elif b >= a:
a = c
else:
pass
return fib
sleep(1.2)
root = Tk()
fib = StringVar()
texxt = StringVar()
root.title("Fibonacci Calculator")
entry = Entry(root, textvariable = texxt)
entry.grid(row = 0, column = 0, sticky = 'nsew', padx = 3, pady = 3)
button = Button(root, text = "Start", command = calc)
button.grid(row = 1, column = 0, sticky='nsew', padx = 3, pady = 3)
label = Label(root, width = 10, textvariable = fib, relief = 'sunken')
label.grid(row = 0, column = 1, rowspan = 2, sticky = 'nsew')
root.mainloop()
I expect the next number to appear every 1.2 seconds, but I just get 1 and nothing else.
I am creating a code to create a calculator but I keep on getting this error:
Traceback (most recent call last):
File "C:\Users\Monish Shah\AppData\Local\Programs\Python\Python36-
32\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "C:\Users\Monish Shah\AppData\Local\Programs\Python\Python36-
32\monish-play\calc-completed-copy-for-editing-copy2.py", line 40, in click
Label (window, text = str(sqrt(n_textentry)), bg = "white") .grid(row = 13,
column = 0, sticky = N)
TypeError: must be real number, not Entry
Does anyone know why my code does not work? I don't really understand why it cannot be and entry since I am collecting the user's input? I was researching but I could not figure out how to correctly incorporate the user's input into the code.
Here is my code that I used:
from math import sqrt
from tkinter import *
window = Tk()
window.title("Welcome to Calculator ")
window.configure(background = "white")
Label (window, text = "Calculator", bg = "white") .grid(row = 0, column = 0,
sticky = N)
#to create the box for the first number and store it
Label (window, text = "Enter the first number", bg = "white") .grid(row = 1,
column = 0, sticky = N)
n_textentry = Entry(window, width = 10, bg = "white")
n_textentry.grid(row = 2, column = 0, sticky = N)
#to create the box for the second number
Label (window, text = "Enter the second number", bg = "white") .grid(row = 5,
column = 0, sticky = N)
m_textentry = Entry(window, width = 10, bg = "white")
m_textentry.grid(row = 6, column = 0, sticky = N)
#click function
def click():
n_textentry.get()
m_textentry.get()
operation_textentry.get()
if operation_textentry == 1:
result1 = Label (window, text = str(n_textentry + m_textentry), bg =
"white") .grid(row = 13, column = 0, sticky = N)
elif operation_textentry == 2:
Label (window, text = str(n_textentry - m_textentry), bg = "white")
.grid(row = 13, column = 0, sticky = N)
elif operation_textentry == 3:
Label (window, text = str(n_textentry * m_textentry), bg = "white")
.grid(row = 13, column = 0, sticky = N)
elif operation_textentry == 4:
Label (window, text = str(n_textentry / m_textentry), bg = "white")
.grid(row = 13, column = 0, sticky = N)
elif operation_textentry == 5:
Label (window, text = str(n_textentry ** m_textentry), bg = "white")
.grid(row = 13, column = 0, sticky = N)
else:
Label (window, text = str(sqrt(n_textentry)), bg = "white")
.grid(row = 13, column = 0, sticky = N)
# operation_textentry == 6:
# Label (window, text = str(sqrt(n_textentry)), bg = "white")
.grid(row = 13, column = 0, sticky = N)
#else:
# print("Invalid Operation ")
#to show list of options
Label (window, text = '''
Enter 1 for addition
Enter 2 for subtraction
Enter 3 for multiplication
Enter 4 for division
Enter 5 for exponentiation
Enter 6 for square root *This will only work for 1st choice*''', bg =
"white") .grid(row = 9, column = 0, sticky = W)
operation_textentry = Entry(window, width = 10, bg = "white")
operation_textentry.grid(row = 10, column = 0, sticky = N)
Button(window, text = "Submit", width = 6, command=click) .grid(row = 11,
column = 0, sticky = N)
There are a number of problems with this code:
You need to store the results of those get calls.
As suggested by Joel, you need to convert them to float or int.
You should create the results Label once at startup, and config the text in this callback, instead of creating a new Label every time the user hits Submit.
Instead of repeating all of the same code 6 times, just calculate a result in the elif chain, and then use it at the end.
I already explained most of this in my answer to your previous question.
The result should look something like this:
result_label = Label(window, text = str(n_textentry ** m_textentry), bg = "white")
result_label.grid(row = 13, column = 0, sticky = N)
def click():
n = int(n_textentry.get())
m = int(m_textentry.get())
operation = int(operation_textentry.get())
if operation == 1:
result = n+m
elif operation == 2:
result = n-m
elif operation == 3:
result = n*m
elif operation == 4:
result = n/m
elif operation == 5:
result = n**m
else:
result = "Invalid Operation"
result_label.config(text=str(result))
As I mentioned before, you probably want some error handling for the case where the user leaves one of the entries blank, or inputs text instead of a number, or divides by zero, etc. The simplest way to do this with a try: around the whole click function:
def click():
try:
n = int(n_textentry.get())
# etc.
except Exception as e:
result_label.config(text=repr(e))
Try converting user input to a float or int. It looks like you're trying to apply a mathematical operation on user input (the Entry object), but mathematical operations are not supported for Entry objects.