I am currently working on a hangman game using tkinter in python.
When I click a button of the letter and it is in the word that we are guessing it should show the letter. But when I click the button this problem is popping up:
This example is only with one button. People say that this problem is because of the mainloop(), but i have no idea how to fix it.
from tkinter import *
from tkinter import messagebox
from generate_word import word
#DEFAULT VALUES
score = 0
count = 0
win_count = 2
WINDOW_BG = '#e5404e'
WINDOW_SIZE = '1200x870+300+80'
FONT = ('Arial', 40)
from tkinter import *
from tkinter import messagebox
from generate_word import word
#DEFAULT VALUES
score = 0
count = 0
win_count = 2
WINDOW_BG = '#e5404e'
WINDOW_SIZE = '1200x870+300+80'
FONT = ('Arial', 40)
#this is an example with only one button
buttons = [['b1','a',80,740]]
#Creating window and configurating it
window = Tk()
window.geometry(WINDOW_SIZE)
window.title('Hangman')
window.config(bg = WINDOW_BG)
#generates all of the labels for the word
def gen_labels_word():
label = Label(window, text = " ", bg = WINDOW_BG, font = FONT)
label.pack( padx = 40,pady = (500,100),side = LEFT)
label1 = Label(window, text = word[0], bg = WINDOW_BG, font = FONT)
label1.pack( padx = 41,pady = (500,100),side = LEFT)
x = 21
for var in range(1,len(word)):
exec('label{}=Label(window,text="_",bg=WINDOW_BG,font=FONT)'.format(var))
exec('label{}.pack(padx = {}, pady = (500,100), side=LEFT)'.format(var,x))
x += 1
exec('label{} = Label(window, text = "{}", bg = WINDOW_BG, font = FONT)'.format(len(word),word[-1]))
exec('label{}.pack( padx = {},pady = (500,100),side = LEFT)'.format(len(word), x+1))
# ---------------------------------------------------------------------------------------------------------------------------------------------------
gen_labels_word()
#----------------------------------------------------------------------------------------------------------------------------------------------------
#letters icons(images)
#hangman (images)
hangman = ['h0','h1','h2','h3','h4','h5','h6']
for var in hangman:
exec(f'{var}=PhotoImage(file="{var}.png")')
han = [['label0','h0'],['label1','h1'],['label2','h2'],['label3','h3'],['label4','h4'],['label5','h5'],['label6','h6']]
for p1 in han:
exec('{}=Label(window, bg = WINDOW_BG ,image={})'.format(p1[0],p1[1]))
exec('label0.place(x = 620,y = 0)')
for var in letters:
exec(f'{var}=PhotoImage(file="{var}.png")')
for var in buttons:
exec(f'{var[0]}=Button(window,bd=0,command=lambda: game_brain("{var[0]}","{var[1]}"),bg = WINDOW_BG,font=FONT,image={var[1]})')
exec('{}.place(x={},y={})'.format(var[0],var[2],var[3]))
def game_brain(button, letter):
global count,win_count,score
exec('{}.destroy()'.format(button))
if letter in word:
for i in range(1,len(word)):
if word[i] == letter:
win_count += 1
exec(f'label{i}.config(text="{letter}")')
if win_count == len(word):
score += 1
messagebox.showinfo('GOOD JOB, YOU WON!\n GOODBYE!')
window.destroy()
else:
count += 1
exec('label{}.destroy()'.format(count-1))
exec('label{}.place(x={},y={})'.format(count,620,0))
if count == 6:
messagebox.showinfo('GAME OVER','YOU LOST!\nGOODBYE!')
window.destroy()
def EXIT():
answer = messagebox.askyesno('ALERT','Do you want to exit the game?')
if answer == True:
window.destroy()
e1 = PhotoImage(file = 'exit.png')
ex = Button(window,bd = 0,command = EXIT,bg = WINDOW_BG,font = FONT,image = e1)
ex.place(x=1050,y=20)
#-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
window.mainloop()
Why exec function in python adds '.!' at the beginning of the text?
The exec function isn't doing that. Tkinter by default names all of its widgets with a leading exclamation point. When you print out a widget, it has to be converted to a string. For tkinter widgets, the result of str(some_widget) is the widget's name.
You can see this quite easily without exec:
import tkinter as tk
root = tk.Tk()
label = tk.Label(root)
print(label)
The above will print something like .!label. If you create a second label it will be .!label2, a third will be .!label3 and so on.
On an unrelated note, you shouldn't be using exec to create widgets. It makes the code very hard to understand and debug. If you want to create widgets in a loop, add them to a dictionary or list instead of dynamically creating variables with exec.
For example:
labels = {}
for var in range(1,len(word)):
label = Label(window,text="_",bg=WINDOW_BG,font=FONT)
label.pack(padx=500, pady=100)
labels[var] = label
With that, you can later reference the widgets as labels[1], labels[2], etc.
You should do the same thing with the images, or anything else that you create in a loop and want to keep track of.
Related
I'm making an mp3 player app and I want the user to be able to add playlists (which will be buttons in a grid system.
Like so.
However in this state when I'm trying to add a playlist button this happens:
The top buttons and the button i just added get squeezed off and the scrollbar is stuck (I can't use it)
I've tried refreshing by self.playlist_frame.update_idletasks() after running create_playlists() but it doesn't seem to change anything
this is my code so you can test it:
import os
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
class MyApp:
def __init__(self):
self.root = tk.Tk()
self.width = self.root.winfo_screenwidth()
self.height = self.root.winfo_screenheight()
self.height = self.root.winfo_screenheight() - int(self.height / 13)
self.root.geometry(f'{self.width}x{self.height}')
self.root.title("Mp3")
self.c = '#14141D'
self.playlists_buttons_list = []
self.playlists = {}
self.helping_d = {}
self.main = ttk.Notebook(self.root)
self.playlists_tab = tk.Frame(self.main)
self.playlist_menu_frame = tk.Frame(self.playlists_tab, bg=self.c)
self.playlists_canvas = tk.Canvas(self.playlists_tab, bg=self.c)
self.playlist_frame = tk.Frame(self.playlists_canvas, bg=self.c)
self.playlists_scrollbar = ttk.Scrollbar(self.playlists_tab, orient='vertical',
command=self.playlists_canvas.yview)
self.add_playlist_win = tk.Toplevel(self.root)
self.add_playlists_button = tk.Button(self.playlist_frame)
self.default_img = Image.open('images/default.png').resize((75, 72))
self.default_img = ImageTk.PhotoImage(self.default_img)
self.add_img = Image.open('images/add.png').resize((50, 50))
self.add_img = ImageTk.PhotoImage(self.add_img)
self.widgets()
self.root.mainloop()
def widgets(self):
self.playlists_tab.place(height=self.height, width=self.width, x=0, y=0)
self.main.add(self.playlists_tab, text="Playlists")
self.main.pack(expand=1, fill="both")
self.playlist_frame = tk.Frame(self.playlists_canvas, bg=self.c)
self.playlists_canvas.create_window((0, 0), window=self.playlist_frame, anchor='center')
self.playlists_canvas.config(bg=self.c)
self.playlists_canvas.place(height=int(self.height / 1.428), width=self.width, x=0, y=int(self.height / 9))
self.add_playlists_button = tk.Button(self.playlists_canvas, image=self.add_img, bg=self.c, bd=0,
command=self.add_playlists)
self.add_playlists_button.place(x=1284, y=75)
self.playlists_scrollbar.pack(side='right', fill='y')
self.playlists_canvas.config(yscrollcommand=self.playlists_scrollbar.set)
self.playlists_canvas.bind('<Configure>', lambda e: self.playlists_canvas.configure(
scrollregion=self.playlists_canvas.bbox('all')))
if os.path.getsize('playlistsnames.txt') != 0:
lines = open('playlistsnames.txt', 'r', encoding='utf-8').read().splitlines()
for i in lines:
self.playlists[i] = []
self.create_playlists()
def create_playlists(self):
self.playlists_buttons_list = []
for i in range(len(self.playlists.keys())):
self.playlists_buttons_list.append(tk.Button(self.playlist_frame, bg=self.c,
text=' ' + list(self.playlists.keys())[i].split('!')[0],
fg='white', image=self.default_img, height=280, width=300))
row = 0
column = 0
for i in self.playlists_buttons_list:
if column > 3:
row += 1
column = 0
if column > 7:
row += 1
column = 0
i.grid(row=row, column=column)
column += 1
def get_name(self):
self.name_of_plst = self.plst_entry.get()
self.playlists[self.name_of_plst] = []
self.create_playlists()
with open('playlistsnames.txt', 'a', encoding='utf-8') as names_file:
names_file.write(self.name_of_plst + '!\n')
def add_playlists(self):
self.add_playlist_win = tk.Toplevel(self.root)
self.name_label = tk.Label(self.add_playlist_win, text='Name:', bg=self.c, fg='pink').pack()
self.plst_entry = tk.Entry(self.add_playlist_win)
self.plst_entry.pack()
self.confirm_btn = tk.Button(self.add_playlist_win, text='Confirm', bg=self.c, fg='pink',
command=self.get_name).pack()
self.add_playlist_win.mainloop()
MyApp()
I should also mention that because I store the playlist names in a file called playlistnames.txt when I rerun the program I can see all the playlist buttons plus the one I created before and things work just fine.
So what I want is making the grid update and work while running the app without having to rerun the app of course
My playlistsnames.txt file has this format:
rock!
metal!
chill!
rap!
jazz!
blues!
hard rock!
trap!
Any suggestions would be helpful!
Thanks in advance!
I am making a point of sale systems and one of the important things I'm trying to make is a total button or just a total. So when the total button is pressed, or every time an item is added, the total will be recalculated and outputted.
I started off with just declaring the variables:
item1_qty = 0
item2_qty = 0
item3_qty = 0
item4_qty = 0
item5_qty = 0
item6_qty= 0
item7_qty = 0
item8_qty = 0
item1_price = 0
item2_price = 0
item3_price = 0
item4_price = 0
item5_price = 0
item6_price = 0
item7_price = 0
item8_price = 0
itemTotal=0
and I'm using this code for the actual item buttons (I have 4 currently just so I don't get confused but I've included the first 2)
#Item1 Button + Function
def item1_Button():
global item1_qty
item1_text = ("Chips")
item1_price = "2.00"
item1_qty += 1
item1_text = (item1_text + " "+item1_price +" "+ str(item1_qty)) #concatonates text & variable
item1.config(text=item1_text) #updates label text - doesn't add multiple
item1.place(x=0, y=0) #places label within the frame
item1_Button = Button(itemFrame, text="Chips", width=10, height=10, command=item1_Button)
#creates button + links to function
item1_Button.grid(row=1, column=1) #positions button
item1 = Label(receiptFrame)#creates label for button
#Item2 Button + Function
def item2_Button():
global item2_qty
item2_text = "Fish & Chips"
item2_price = "5.00"
item2_qty += 1
item2_text = (item2_text + " "+item2_price +" "+ str(item2_qty)) #concatonates text & variable
item2.config(text=item2_text) #updates label text - doesn't add multiple
item2.place(x=0, y=50)
item2_Button = Button(itemFrame, text="Fish & Chips", width=10, height=10, command=item2_Button)
item2_Button.grid(row=1, column=2)
item2 = Label(receiptFrame)
I'm not entirely sure what I'm doing in terms of the actual total button, so I have kind of just started off with this:
def updateTotal():
global item1_price, item2_price, item3_price, item4_price
global item1_qty, item2_qty, item3_qty, item4_qty
itemPrice = item1_price + item2_price + item3_price + item4_price
itemQuantity = item1_qty + item2_qty + item3_qty + item4_qty
itemTotal = (itemPrice * itemQuantity)
totalPrice.config(text=str(itemTotal))
totalPrice = Label(totalFrame, font=("arial", 25), text="0"+str(itemTotal))
totalPrice.place(x=10, y=10)
totalButton = Button(totalFrame, text="Total", command=updateTotal, width=15, height=5)
totalButton.place(x=450, y=0)
Is there something I need to do different in terms of how I use the variables?
I would prefer if the total was just text that updated every time an Item button was clicked instead of a Total button but I would appreciate some guidance as nothing is happening at the moment and I'm not entirely sure what I need to do as I'm fairly new with tkinter.
You didnt provide a minimal reproducible example and this post is 2 months old so im not gonna really explain the code and it is mostly self explanatory.
used f-strings, they are like this:
intensifier = input("Enter an intensifier: ")
adjective = input("Enter an adjective: ")
sentence = f"This is {intensifier} {adjective}"
print(sentence)
The curly-brackets let you enter none string values and are automatically turned into a string. (Useable in most scenarios and no concatenation)
Changed items into lists, lists can be used like this:
list1 = ["value1",2,[value3]]
print(list1[0]) #Would print, "value1"
print(list1[1:2])#Would print, [2,[value3]]
Listbox is a box of lists, you can add stuff into it using
listbox.insert(<place to add to>, <object to add>)
Full Code:
from tkinter import *
root = Tk()
root.title("Receipt System")
items_qty = [0,0,0,0]
items_price = [2,5,0,0]
items_text = ["Chips","Fish&chips","",""]
global itemTotal #Global variable
items_total = [0,0,0,0]
currency = "£" #Sets currency
#Functions
def addItem(x):
items_qty[x] += 1 #Adds 1
totalPrice = items_qty[x] * items_price[x] #quantity * price
text = f"{items_qty[x]}x {items_text[x]} {currency}{totalPrice}"
for i in range(0, receipt.size()):
if items_text[x] in receipt.get(i):
receipt.delete(i)
receipt.insert(END, text)
updateTotal(x)
def updateTotal(x):
global items_total
items_total[x] = items_qty[x] * items_price[x] #quantity * price
total = 0
for i in range(0, len(items_total)):
total += items_total[i]
totalPrice.config(text=f"Total: {currency}{str(total)}")
#UI
itemFrame = Frame(root)
itemFrame.pack(pady = 10)
receiptFrame = Frame(root)
receiptFrame.pack(pady = 10)
item1Btn = Button(itemFrame, text=f"Chips - {currency}{items_price[0]}", command=lambda: addItem(0)).pack(side = LEFT, padx = 10)
item2Btn = Button(itemFrame, text=f"Fish & Chips - {currency}{items_price[1]}", command=lambda: addItem(1)).pack(side = LEFT, padx =10)
receipt = Listbox(receiptFrame, selectmode = SINGLE, width = 30) #Receipt list
receipt.pack()
totalPrice = Label(receiptFrame, text = f"Total: {currency}0")
totalPrice.pack(pady = 10)
root.mainloop()
Code:
import tkinter as tk
main = tk.Tk()
main.wm_title("Flashcards")
terms = []
defs = []
count = 0
configuration = tk.Frame(bg = "sky blue")
configuration.grid(row = 0, column = 0)
flashcards = tk.Frame(bg = "sky blue")
flashcards.grid(row = 0, column = 0)
flashcard = tk.Button(flashcards, font = ("Dotum", 12))
finish = tk.Label(flashcards, font = ("Dotum", 24), border = 10)
def createflashcard():
terms.append(ent_term.get())
defs.append(ent_def.get())
ent_term.delete(0, "end")
ent_def.delete(0,"end")
def studyset():
global count
global finish
global flashcard
if count == 0:
finish.pack_forget()
flashcards.tkraise()
flashcard.pack(side = "top")
configuration.grid_forget()
flashcard.configure(text = terms[count], command = replaceflashcard)
print(terms)
def replaceflashcard():
global count
global flashcard
global finish
flashcard.configure(text = defs[count])
if count == (len(terms) - 1):
finish.pack(side = "top")
finish.configure(text = "End of Set!")
flashcard.configure(command = endset)
else:
count = count + 1
flashcard.configure(command = studyset)
def endset():
global count
global finish
global flashcard
flashcard.configure(text = "Restart set", command = studyset)
count = 0
ent_term = tk.Entry(configuration, text = "Enter your term", font = ("Dotum", 12))
ent_term.pack(side = "top")
ent_def = tk.Entry(configuration, text = "Enter definition", font = ("Dotum", 12))
ent_def.pack(side = "top")
create = tk.Button(configuration, text = "Create
flashcard", font = ("Dotum", 12), command = createflashcard)
create.pack(side = "top")
studyset = tk.Button(configuration, text = "Study set", font = ("Dotum", 12), command = studyset)
studyset.pack(side = "top")
main.mainloop()
I am attempting to write a program that allows you to create flashcard sets. When studying the flashcards, the tkinter program packs a button which the user can click on to switch from the term to its definition. When the user clicks on the button again, the program is meant to switch to the next term in the list.
The program runs as it should up until count is incremented within the replaceflashcard() function. It doesn't execute the studyset() function as I coded in. I click the flashcard button repeatedly yet it stays stuck displaying the definition.
I am trying to create a simple hangman game using tkinter with python. While creating the GUI, the first thing I put in were the dashes.lbl = tkinter.Label(window, text= xhash). When I put in my buttons, the label disappeared. I have tried making the window bigger, but it still didn't come back.
Here is my full code:
#importing modules
import tkinter
import random
#creating the dashes
f = open(r'C:\Users\Gareth\Documents\hangman\words.txt', 'r')
word = f.readlines()
guess = random.choice(word)
hash = []
xhash = ""
while len(hash) < len(str(guess)):
hash.append('_ ')
def rejoin():
xhash = "".join(hash)
xhash.strip("[]")
xhash.strip(",")
xhash.strip("'")
#creating the window
window = tkinter.Tk()
window.minsize(300, 300)
#commands for buttons a-z
def acmd():
for i in list(guess):
if i == 'a':
hash[guess.index(i)] = 'a'
rejoin()
lbl.configure(text= xhash)
abtn.configure(bg = 'red')
#label
lbl = tkinter.Label(window, text= xhash)
#buttons for letters a-z
abtn = tkinter.Button(window, text = 'a', command = acmd)
bbtn = tkinter.Button(window, text = 'b')
cbtn = tkinter.Button(window, text = 'c')
dbtn = tkinter.Button(window, text = 'd')...
#placing lbl and buttons
lbl.grid(row = 1, column = 1)
abtn.grid(row = 3 , column = 1)
bbtn.grid(row = 3 , column = 2)
cbtn.grid(row = 3 , column = 3)...
window.mainloop()
You would need to position them correctly, most probably either you are not positioning them or positioning them on top of each other. You would need to use grid() to position them
Example -
lbl = tkinter.Label(window, text= xhash)
lbl.grid(row=0,column=1)
abtn = tkinter.Button(window, text = 'a', command = acmd)
abtn.grid(row=0,column=2)
The problem did not lie within the label, but within the text.
def rejoin():
xhash = "".join(hash)
xhash.strip("[]")
xhash.strip(",")
xhash.strip("'")
the only adjustment needed was a global statement...
without that, whatever was done in the function had nothing to do with anything else, so, the previously assigned xhash = '', was simply left blank...
import Tkinter
import tkMessageBox
from Tkinter import *
CanvasHeight = 500
CanvasWidth = 600
Canvas width and height set to 10x the maximum of the variables.
IsGraphHidden = 0
MainWindow = Tkinter.Tk()
This is the window for all the sliders, and is defined as "MainWindow" for later use.
Strength = DoubleVar()
Multiple = DoubleVar()
Time = DoubleVar()
All of the variables set to DoubleVar, because of the Tkinter plugin.
It needs it's own special floats, integers and strings to work.
They can be accessed as normal variables by using VARIABLE.get()
coords = []
lastcoords = [0,0]
This is what we'll use to continue the line instead of just having a bunch of lines drawing themselves from the corner of the screen.
Plot = DoubleVar()
StrengthScale = Scale( MainWindow, variable = Strength, orient = HORIZONTAL,label="Strength")
MultipleScale = Scale( MainWindow, variable = Multiple, from_ = float(0), to = float(1), resolution = float(0.01), orient = HORIZONTAL, label="Multiple")
TimeScale = Scale( MainWindow, variable = Time, orient = HORIZONTAL, from_ = int(0), to = int(120), label="Time")
These are the procedures for the buttons, as well as the rest of the code.
def Calculate():
answer = float(Strength.get())*float(Multiple.get())
tkMessageBox.showinfo("Answer:", answer)
def PrepPlot():
global IsGraphHidden
global coords
global lastcoords
lastcoords0 = lastcoords[0]
lastcoords1 = lastcoords[1]
coords.append(lastcoords0)
coords.append(lastcoords1)
coords.append(Time.get()*5)
coords.append(Strength.get()*Multiple.get()*5)
lastcoords = Time.get()*5
lastcoords = Strength.get()*Multiple.get()*5
if IsGraphHidden == 0:
Graph = Canvas(MainWindow, width = CanvasWidth, height = CanvasHeight, bg = "white")
Graph.create_line(coords, fill = "black")
Graph.grid(row=5, column=1)
else:
Graph.destroy()
Graph.delete("all")
Graph.create_line(coords, fill = "black")
Graph.grid(row=5,column=1)
IsGraphHidden = 1
def DisplayPoints():
PointWindow = Tkinter.Tk()
Text = Label(PointWindow, textvariable = "Hi there", relief=RAISED)
Text.pack()
PointWindow.mainloop() #Work in progress, nothin' to see here.
Button = Tkinter.Button(MainWindow, text= "Calculate",command = Calculate)
PrepButton = Tkinter.Button(MainWindow, text = "Plot", command = PrepPlot) #The text is the text on the button.
DisplayButton = Tkinter.Button(MainWindow, text = "Display Plots", command = DisplayPoints)
MultipleScale.grid(row=0,column=0)
StrengthScale.grid(row=1,column=0)
TimeScale.grid(row=1,column=2)
PrepButton.grid(row=2,column=1)
Button.grid(row=4,column=1)
DisplayButton.grid(row=3,column=1)
MainWindow.mainloop()
I need some help with the float object getitem error, I'm doing this code for work experience at Manchester university...
You replaced the lastcoords list with a floating point value:
lastcoords = Time.get()*5
lastcoords = Strength.get()*Multiple.get()*5
so that next time around the line:
lastcoords0 = lastcoords[0]
raises your exception as you cannot use subscription on a floating point value.
I think you wanted to set a new list instead:
lastcoords = [Time.get() * 5, Strength.get() * Multiple.get() * 5]