This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 1 year ago.
I'm attempting to make a GUI for my text game (similar to a pokemom game)
In summary the code works like:
import random
hp = 100
for i in range (0,4):
element = input("Enter attack: ")
list.append(element)
attack1 = list[0]
attack2 = list[1]
attack3 = list[2]
attack4 = list[3]
while(True):
cmd = input(">")
If cmd.lower() == attack1 ... :
if cmd.lower() == bite:
hp -= 10
Now I tried doing a gui on Tkinter through buttons but it seems I can't get a grasp of it
from tkinter import *
#4 buttons
bite = 0
root = Tk()
def click(hp):
hp -= 10
myLabel = Label(text=f"{hp} hp remaining left.")
myLabel.grid(row = 5, column = 5)
def click2():
bite = 0
hp = 50
hp -= 20
myLabel = Label(text=f"{hp} hp remaining left.")
myLabel.grid(row = 5, column = 5)
bite += 1
myButton = Button(root, text=f"hello {bite}/5", command = click(hp))
myButton2 = Button(root, text=f"Presssss {bite}/5", command = click2)
myButton3 = Button(root, text="Presssss")
myButton4 = Button(root, text="Presssss")
myButton.grid(row = 0, column = 1)
myButton2.grid(row = 1, column = 1)
myButton3.grid(row = 1, column = 2)
myButton4.grid(row = 0, column = 2)
root.mainloop()
And this just presents a constant value "90, 30" due to the variables being included on the function, but whenever I'm trying to put it into an argument like
hp = 100
def click(hp):
hp -= 10
button1 = Button(root, text = "try", command = click(100))
It just returns the value 90, 30 way before the button is clicked. When hp is used as an arg, it is saying undefined.
It does so because click() function is called as soon as window is created.
To prevent it you can use lambda :
myButton = Button(root, text=f"hello {bite}/5", command = lambda: click(hp))
Related
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.
I've currently been creating a math equation generator program that will ask you random multiplication questions. I have been having trouble with the if statements where ans == answer will be equal dependent on the user's input (correct answer). However, my program does not see it as equal despite being the same value with example from printing ans and answer which shows they are the same value. I don't know if I am doing something wrong and I would like to know a method of fixing my issue.
Also when I tried to create for and while loops for the generating the equations, it would print them all out at once, is there a way to also make it so that the loop will not print a new random equation until the user gets the answer right?
from tkinter import *
from random import *
import random
import time
import tkinter as tk
import math
import operator
from tkinter import messagebox
#This is for creating the tkinter window and fixing the specified dimensions into place
root = tk.Tk()
root.geometry("900x600")
#This section creates the canvas and its specifications
canvas_width = 900
canvas_height = 500
c = Canvas(root, width=canvas_width, height=canvas_height, bg="pink")
c.pack()
def quitgame():
root.destroy()
class Game_Images:
#Background Image
bg = PhotoImage(file="../Data/sidescroll background flip.gif")
bg = bg.zoom(2)
c.create_image(0,0, anchor=NW, image=bg)
#Insert Image of Enemy
enemy = PhotoImage(file="../Data/monke2.png")
enemy1 = c.create_image(0,260, anchor=NW, image=enemy)
#Insert image of playable character
player = PhotoImage(file="../Data/monke2.png")
player1 = c.create_image(0,325, anchor=NW, image=player)
g = Game_Images()
score = 0
x = 1
def game_start():
global answer, question
int_1 = random.randint(1, 12)
int_2 = random.randint(1, 12)
displayQuestion = "What is "+str(int_1)+ "*" + str(int_2)+"?"
operator = ["*"]
ops = random.choice(operator)
c.create_rectangle(353,0,550,75, fill = "white")
c.create_text(450, 50, font = ("Helvetica", 15), fill="pink", text = displayQuestion)
question = str(int_1) + str(ops) + str(int_2)
answer = int_1 * int_2
def generateQ():
ans = e1.get()
e1.delete(0, END)
if ans == answer:
score += 1
x += 1
print("correct")
print(ans)
print(answer)
else:
print("wrong")
print(ans)
print(answer)
#Buttons show up below the canvas to run commands when pressed
Button(root, text = "Commence Forth",width = 15, command = game_start).place(x=10, y=570)
Button(root, text = "Quit",width = 11, command = quitgame).place(x=800, y=570)
e1 = Entry(root)
e1.pack(padx=30, pady=30)
b=Button(root,text="Enter", width=5, font=("Helvetica", 12), command = generateQ)
b.place(x=550, y=534)
root.mainloop()
Change function generateQ() like this -
def generateQ():
global score,x
ans = e1.get()
e1.delete(0, END)
if int(ans) == int(answer):
score += 1
x += 1
print("correct")
print(ans)
print(answer)
else:
print("wrong")
print(ans)
print(answer)
Use int() so that they are of same data type. And use global score,x because it shows error. You could also write score and x as arguments.
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()
So I've been teaching myself some tkinter to start building some real apps, and my first project is to build an interface for the Courera's all-too-famous Rock-Paper-Scissors-Lizard-Spock game.
Up to now I have all the buttons working fine enough (even though I feel like they are not updating anything if i repeatedly click on the same button without changing choice, as the result of the match never changes) and the result panel also works. that's what's makng me crazy, as far as I can see, the win counters and the computer choice panel follow the same logic and for some reason are not updating when I click a button. any hints?
Thanks already for the patience, and code as follows
import tkinter as tk
import random
from functools import partial
#setting the window early as I had issues and bugs when called after defining functions
window = tk.Tk()
window.geometry('350x200')
#global variables
result= tk.StringVar()
result.set("")
comp = tk.IntVar()
guess = tk.StringVar()
guess.set("")
playerWin = tk.IntVar()
playerWin.set(0)
compWin = tk.IntVar()
compWin.set(0)
#function that handles the computer's play in each game
def compPlay():
global guess, comp
comp.set(random.randrange(0,5))
if comp.get()== 0:
guess.set("Rock")
elif comp.get()== 1:
guess.set("Spock")
elif comp.get() == 2:
guess.set("Paper")
elif comp.get() == 3:
guess.set("Lizard")
elif comp.get() == 4:
guess.set("Scissors")
#function to play human vs computer choices and see who wins
def gameplay(playerNum,compNum):
global result, comp, playerWin, compWin
if playerNum == comp.get():
result.set("It's a tie!")
elif (playerNum - comp.get()) % 5 <= 2:
result.set("Player wins!")
playerWin = playerWin.get() + 1
elif (playerNum - comp.get()) % 5 >= 3:
result.set("Computer wins!")
compWin += compWin.get() + 1
else:
result.set(text = "")
# game title
lblGame= tk.Label(text="Rock, Scissors, Paper, Lizard, Spock").pack()
#frame with the buttons for player choices
playerFrame = tk.Frame(window)
btRock = tk.Button(playerFrame, text = "Rock", width = 15, command = partial(gameplay, 0,compPlay)).pack()
btScissors = tk.Button(playerFrame, text = "Scissors", width = 15, command = partial(gameplay, 1,compPlay)).pack()
btPaper = tk.Button(playerFrame, text = "Paper", width = 15, command = partial(gameplay, 2,compPlay)).pack()
btLizard = tk.Button(playerFrame, text = "Lizard", width = 15, command = partial(gameplay, 3,compPlay)).pack()
btSpock = tk.Button(playerFrame, text = "Spock", width = 15, command = partial(gameplay, 4,compPlay)).pack()
playerFrame.pack(side = tk.LEFT)
#frame with info about the game, as in what the computer chose and the result of the play
compFrame = tk.Frame(window)
lbComp = tk.Label(compFrame, text = "Computer plays:").pack()
lbGuess = tk.Label(compFrame, textvariable = guess, relief = tk.GROOVE, borderwidth = 5, width = 15).pack()
lbRes = tk.Label(compFrame, text = "and the result of the game is").pack()
lbMatch = tk.Label(compFrame, textvariable = result, relief = tk.GROOVE, borderwidth = 5, width = 15).pack()
#mini frames for score keeping
playerFrame = tk.Frame(compFrame, relief = tk.GROOVE, borderwidth = 3)
playerSide = tk.Label(playerFrame, text = "Player points:").pack()
playerScore = tk.Label(playerFrame, textvariable = str(playerWin)).pack()
playerFrame.pack(side = tk.LEFT)
compScoreFrame = tk.Frame(compFrame, relief = tk.GROOVE, borderwidth = 3)
compSide = tk.Label(compScoreFrame, text = "Computer points:").pack()
compScore = tk.Label(compScoreFrame, textvariable = str(compWin)).pack()
compScoreFrame.pack(side = tk.RIGHT)
compFrame.pack(side = tk.RIGHT)
window.mainloop()
I get this error on the console whenever the game should give points to either player:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "~/Interactive Python/teste tkinter3.py", line 54, in gameplay
playerWin = playerWin.get() + 1
AttributeError: 'int' object has no attribute 'get'
That's because playWin is a tkinter.Intvar object,you need to change the function gameplay to:
def gameplay(playerNum, compNum):
global result, comp, playerWin, compWin
if playerNum == comp.get():
result.set("It's a tie!")
elif (playerNum - comp.get()) % 5 <= 2:
result.set("Player wins!")
playerWin.set(playerWin.get() + 1)
elif (playerNum - comp.get()) % 5 >= 3:
result.set("Computer wins!")
compWin.set(compWin.get() + 1)
else:
result.set(text="")
There are several problems that are not working properly here:
The Computer plays label field is not refreshing, because you never call the compPlay() function. This function should be called each time the player presses the left-hand button, but this function is unused in the gameplay method. Simply call this function to refresh the computer guess and set the value of a label.
In the gameplay function the compWin and playerWin objects are not ints but tkinter.Intvar so you should set their variables instead of using + and +=. This is the reason for this error.
I have a bit of code in a loop that outputs 2 buttons and a label, but after that it continues, but I want it to not do anything until the user hits a button. If any of you know how to do that it would help a ton. I have no commands in it since I am not sure how I will do this.
"more" is if the user wants another Flashcard, with 1 being yes and 0 being no.
x is the flash card you are on.
while more == "1":
if x <= 1:
self.flashCardText = Label(self.flashcards, text = flashcard[x][y]).grid(row = 1, column = 2)
self.flipButton = Button(self.flashcards, text = "Flip", command = lambda: flip()).grid(row = 2, column = 1)
self.nextButton = Button(self.flashcards, text = "Next", command = lambda: x += 1).grid(row = 2, column = 3)
At the moment you are creating lots of buttons in a while loop. So you can do it like that. What you can do, is to create some sort of dialog (example below). Can't say much without full code of yours, but the example below should be useful:
from tkinter import *
top = Tk()
class Test(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.x = 0
flashCardText = Label(self, text = print('flashcard')).grid(row = 1, column = 2)
flipButton = Button(self, text = "Flip", command = lambda: print('flip')).grid(row = 2, column = 1)
nextButton = Button(self, text = "Next", command = self._test_output).grid(row = 2, column = 3)
self.grid()
def _test_output(self):
if self.x < 2:
print("increasing x")
self.x +=1
else:
print("x is 2")
self.master.destroy()
t = Test(top)
top.mainloop()
So basically, in this example the Test window is being showed until self.x gets to 2. 'self.x' is being increased with each press of Next button. In this example other buttons don't do much.