Python tkinter binding a function to a button - python

from tkinter import *
root = Tk()
root.title("Tip & Bill Calculator")
totaltxt = Label(root, text="Total", font=("Helvitca", 16))
tiptxt = Label(root, text="Tip (%)", font=("Helvitca", 16))
peopletxt = Label(root, text="people", font=("Helvitca", 16))
totaltxt.grid(row=0, sticky=E)
tiptxt.grid(row=1, sticky=E)
peopletxt.grid(row=2, sticky=E)
totalentry = Entry(root)
tipentry = Entry(root)
peopleentry = Entry(root)
totalentry.grid(row=0, column=2)
tipentry.grid(row=1, column=2)
peopleentry.grid(row=2, column=2)
ans = Label(root, text = "ANS")
ans.grid(row=4)
def answer(event):
data1 = totalentry.get()
data2 = tipentry.get()
data3 = peopleentry.get()
if tipentry.get() == 0:
ans.configure(str((data1/data3)), text="per person")
return
elif data1 == 0:
ans.configure(text="Specify the total")
return
elif data3 == 0 or data3 ==1:
ans.configure(str(data1*(data2/100+1)))
return
elif data1 == 0 and data2 == 0 and data3 ==0:
ans.configure(text = "Specify the values")
return
else:
ans.configure(str((data1*(data2/100+1)/data3)), text="per person")
return
bf = Frame(root)
bf.grid(row=3, columnspan=3)
calc = Button(bf, text ="Calculate", fg = "black", command = answer)
calc.bind("<Button-1>", answer)
calc.grid(row=3, column=2)
root.mainloop()
I'm trying to make a tip and bill calculator with a simple design just to learn and experiment. However, I encountered a horrible problem that kept haunting for days, I usually struggle with functions in python and I'm trying to bind a function to a calculate button, which I managed to make it appear. However, I can't manage to get it to work. After some messing around I ended with this error, when I click the calculate button.
This is the error after I click the calculate button:
TypeError: answer() missing 1 required positional argument: 'event'

Commands bound to a button do not get an argument, as the nature of the event is already known. Delete 'event'.
You also bind the answer function to an event. The result is that answer is called both without and with an event argument. Get rid of the bind call.
Follow hint given by Bryan. Stop passing a digit string to .configure as a positional parameter. tk will try to interpret is as dictionary. Instead, add the number string to the rest of the label string.
Like rows, columns start from 0.
The frame is not needed.
The following revision works.
from tkinter import *
root = Tk()
root.title("Tip & Bill Calculator")
totaltxt = Label(root, text="Total", font=("Helvitca", 16))
tiptxt = Label(root, text="Tip (%)", font=("Helvitca", 16))
peopletxt = Label(root, text="people", font=("Helvitca", 16))
totaltxt.grid(row=0, column=0, sticky=E)
tiptxt.grid(row=1, column=0, sticky=E)
peopletxt.grid(row=2, column=0, sticky=E)
totalentry = Entry(root)
tipentry = Entry(root)
peopleentry = Entry(root)
totalentry.grid(row=0, column=1)
tipentry.grid(row=1, column=1)
peopleentry.grid(row=2, column=1)
ans = Label(root, text = "ANS")
ans.grid(row=4, column=0, columnspan=2, sticky=W)
def answer():
total = totalentry.get()
tip = tipentry.get()
people = peopleentry.get()
if not (total and tip):
ans['text'] = 'Enter total and tip as non-0 numbers'
else:
total = float(total)
tip = float(tip) / 100
people = int(people) if people else 1
ans['text'] = str(round(total * tip / people, 2)) + " per person"
calc = Button(root, text ="Calculate", fg = "black", command = answer)
calc.grid(row=3, column=1)
root.mainloop()

Related

Can't get the value of a radio-button in Python/Tkinter

I'm writing a simple unit converter where the user can pick which units they want to convert from two options. I'm using radio-buttons for the choice, but can't seem to get the value of the chosen one to work in the conditions at the bottom of the program.
I tried several solutions suggested here on stack overflow, but none of them worked. At one point, I got the selected() to print the value of the button, but it still didn't work in the condition. Am I missing something obvious here?
Please, note, the converter is not finished yet, there is still some more polishing to do after this issue is solved.
from tkinter import *
window = Tk()
window.title("Unit converter")
window.minsize(width=300, height=300)
window.config(padx=50, pady=50)
def lbs_kgs():
user_input = float(unit_A1.get())
result = round((user_input / 2.2046), 2)
unit_B1.config(text= f"{result}")
def mil_km():
user_input = float(unit_A1.get())
result = round((user_input * 1.6), 2)
unit_B1.config(text= result)
def selected():
return radio_state.get()
intro_label = Label(text = "What units would you like to convert?")
intro_label.grid(column=0, row=0, columnspan=4, pady=10)
radio_state = StringVar()
radiobutton1 = Radiobutton(text="Pounds to kilograms", value="pk", variable=radio_state, command=selected)
radiobutton2 = Radiobutton(text="Miles to kilometers", value="mk", variable=radio_state, command=selected)
radiobutton1.grid(column=0, row=1, columnspan=4)
radiobutton2.grid(column=0, row=2, columnspan=4)
instructions_label = Label(text = "Enter the number:")
instructions_label.grid(column=0, row=3, columnspan=4, pady=10)
unit_A1 = Entry(width=5)
unit_A1.grid(column=1, row=4, sticky="e")
unit_A1_label = Label(text = "unit A1")
unit_A1_label.grid(column=2, row=4, sticky="w")
equal_label = Label(text = "is equal to")
equal_label.grid(column=1, row=5, sticky="e")
unit_B1 = Label(text = "0")
unit_B1.grid(column=2, row=5, sticky="w")
unit_B1_label = Label(text = "result unit")
unit_B1_label.grid(column=3, row=5, sticky="w")
button = Button(text="Calculate")
button.grid(column=0, row=6, columnspan=4, pady=10)
if selected() == "pk":
button.config(command=lbs_kgs)
elif selected() == "mk":
button.config(command=mil_km)
window.mainloop()
Move the if/else check into the selected function so the conditions can be checked each time the selection changes
def selected():
selection = radio_state.get()
if selection == "pk":
button.config(command=lbs_kgs)
elif selection == "mk":
button.config(command=mil_km)
In line 29 should be radio_state = StringVar(window, '1'). Without this when executed both radiobutton are on, but that not right.
def selected():
if (selection := radio_state.get()) == "pk":
button.config(command=lbs_kgs)
elif selection == "mk":
button.config(command=mil_km)
Output:
Output pound to Kilograms:
Output Miles to Kilometers:

Updating Tkinter text with label

I'm trying to make a GUI version of a drinking game, which involves throwing dice. I 'throw the dice' virtually, but want to display the outcome on the screen. I thought using Label() would be most useful, but i'm having trouble updating that text (the throw_text variable) when a new dice throw is done.
Instead of the text updating on the screen, a new text appears below the previous one with the new throw.
I don't want a cascading stream of text, but just one singular text that updates at the dice being thrown i.e. when the 'Roll!' button is pressed.
I've seen people do it using the Stringvar(), however i must be doing it wrong as it does not have an effect on the appearance.
from tkinter import *
dim = [900, 600]
root = Tk()
root.geometry(f"{dim[0]}x{dim[1]}")
root.title("Mex")
root.iconbitmap("dice.ico")
lbl = Label(root, font=("Times", 200))
num = ['\u2680', '\u2681', '\u2682', '\u2683', '\u2684', '\u2685']
d1 = None
d2 = None
throw_txt = StringVar()
def roll_dice():
global d1, d2
d1 = random.choice(num)
d2 = random.choice(num)
lbl.config(text=f"{d1}{d2}")
lbl.pack()
lbl.place(x=dim[0] / 3.8, y=50)
print("Throw:", check_throw(convert_symbol()))
throw_txt = str(check_throw(convert_symbol()))
txt = Label(root, text=throw_txt)
txt.pack(side=TOP)
def convert_symbol():
d1_num = None
d2_num = None
for n in num:
if n == d1:
d1_num = num.index(n) + 1
if n == d2:
d2_num = num.index(n) + 1
if d2_num > d1_num:
throw = f"{d2_num}{d1_num}"
else:
throw = f"{d1_num}{d2_num}"
return throw
def check_throw(t):
num_t = int(t)
if num_t == 21:
return "Mex"
elif num_t % 11 == 0:
return f"Koning: {int(num_t / 11 * 100)}"
elif num_t % 31 == 0:
return "Slok uitdelen"
else:
return str(num_t)
roll_btn = Button(root, text="Roll!", width=10, command=roll_dice)
roll_btn.config(font=("Bahnschrift", 20))
roll_btn.pack()
roll_btn.place(x=dim[0] / 2.5, y=25)
roll_btn = Button(root, text="Convert", width=10, command=convert_symbol)
roll_btn.config(font=("Bahnschrift", 20))
roll_btn.pack()
roll_btn.place(x=dim[0] / 2.5, y=500)
root.mainloop()
You could use the configure attribute instead of storing the text of the Label widget into a separate variable.
Take a quick look at this code:
from tkinter import *
root = Tk()
root.title("my game")
def change_text():
mylabel.configure(text="You just hit the button!")
mylabel = Label(root, text="Hit the button pls")
mylabel.grid(column=0, row=1, sticky='w')
mybutton = Button(root, text="Click me", command=change_text)
mybutton.grid(column=0, row=2, sticky='w')
root.mainloop()

How do I get the values assigned to the Correct and Total numbers to update when the user inputs their answer?

I have a Python file that when run, opens a window for simple addition practice. It asks the user for their input, and if the total is correct will output "Right!" and "Oops!" for incorrect. Below all of this is a counter that keeps track of the correct number out of the total. However, at the moment, those numbers both remain zero when user enters their input. What kind of changes would need to be made under the ClicktheButton1 function in order to get this program properly functioning? Thanks.
The output would end up looking like "2 out 4 correct" in the window, updating after each new problem is solved.
from tkinter import *
import random as rn
window = Tk()
window.geometry('350x350')
window.title("C200")
x = rn.randint(0,100)
y = rn.randint(0,100)
correct, incorrect = 0,0
myLabel = Label(window, text="{0}+{1}=".format(x,y), font=("Arial Bold", 15))
myLabel.grid(column=0, row=0)
myLable2 = Label(window, text = "",font=("Arial Bold", 15))
myLable2.grid(column=0, row=5)
mylabel3 = Label(window,text = "0 out of 0 correct",font=("Arial Bold", 15))
mylabel3.grid(column=0, row=10)
mytxt = Entry(window, width=12)
mytxt.grid(column=1,row=0)
def ClicktheButton1():
global x
global y
global correct
global incorrect
myguess = int(mytxt.get())
if x + y == myguess:
myLable2.configure(text = "Right!")
correct += 1
else:
myLable2.configure(text = "Oops!")
incorrect += 1
x = rn.randint(0,100)
y = rn.randint(0,100)
mytxt.focus()
mytxt.delete(0,END)
myLabel.configure(text = "{0}+{1}=".format(x,y))
btn1 = Button(window, text="check", command = ClicktheButton1)
btn1.grid(column=0, row=7)
def ClicktheButton2():
window.destroy()
btn1 = Button(window, text="Quit", command = ClicktheButton2)
btn1.grid(column=400, row=400)
window.mainloop()
You have to change text in mylabel3 in the same why as you change text in myLabel - and even in the same place. I don't know why you have problem with this.
myLabel.configure(text = "{0}+{1}=".format(x,y))
mylabel3.configure(text="{0} of {1} correct".format(correct, correct+incorrect))

Tkinter questionnaire based on get() method

I am trying to make my first GUI script based on the answers of 2 questions. I'll show an example of the non GUI script
while True:
ammount = input("What is your debt")
if ammount.isdigit() == True:
break
else:
print("Please try typing in a number")
while True:
month = input("In which month did the debt originate?")
if month not in ["January", "February"]:
print("Try again")
else:
break
The point of this script is that it is scalable with all the questions one may add, I want to understand it in the same way in Tkinter. I'll show what I've tried:
from tkinter import *
def click():
while True:
entered_text = text_entry.get()
if entered_text .isdigit() == False:
error = Label(window, text="Try again", bg = "black", fg="red", font="none 11").grid(row = 3, column = 2, sticky= W).pack()
else:
break
return True
window = Tk()
window.title("Tax calculator")
window.configure(background="black")
monto = Label(window, text="¿What is your debt?", bg="black", fg="white", font="none 12 bold")
monto.grid(row = 1, column = 0, sticky= W)
text_entry = Entry(window, width = 20, bg="white")
text_entry.grid(row = 2, column=2, sticky=W)
output = Button(window, text = "Enter", width = 6, command = click)
output.grid(row = 3, column = 0, sticky = W)
The thing is, I can't add a Label() method in the if/else of the click() method, because I would like to ask a new question once the condition is met. I can't also can't get True from click once the condition is met, because the input comes from Button() method. Thanks in advance
You don't actually need any loops here, a simple if statement would be enough to do the trick. Also, there is no need to recreate the label every time, you can just configure() its text. And, note that indexing starts from 0 - so, in your grid, actual first row (and column) needs to be numbered 0, not 1. Besides that, I suggest you get rid of import *, since you don't know what names that imports. It can replace names you imported earlier, and it makes it very difficult to see where names in your program are supposed to come from. You might want to read what PEP8 says about spaces around keyword arguments, as well:
import tkinter as tk
def click():
entered_text = text_entry.get()
if not entered_text.isdigit():
status_label.configure(text='Please try again')
text_entry.delete(0, tk.END)
else:
status_label.configure(text='Your tax is ' + entered_text)
text_entry.delete(0, tk.END)
root = tk.Tk()
root.title('Tax calculator')
root.configure(background='black')
monto = tk.Label(root, text='¿What is your debt?', bg='black', fg='white', font='none 12 bold')
monto.grid(row=0, column=0, padx=10, pady=(10,0))
text_entry = tk.Entry(root, width=20, bg='white')
text_entry.grid(row=1, column=0, pady=(10,0))
status_label = tk.Label(root, text='', bg='black', fg='red', font='none 11')
status_label.grid(row=2, column=0)
button = tk.Button(root, text='Enter', width=17, command=click)
button.grid(row=3, column=0, pady=(0,7))
root.mainloop()
I forgot to mention, if your application gets bigger, you might be better off using a class.

How do I make this button update a balance?

I have this code and basically what I want to do is I want that on pressing the button the balance at the button is updated with the amount. If the balance is currently 15, and I add 10, I want it to add 10 to it.
from tkinter import *
def bal():
ans = int (input1.get ())
total = IntVar ()
tot = int (total.get ())
tot = tot + ans
res.set(tot+ans)
root = Tk()
root.geometry("1280x720")
upper = Frame(root)
upper.pack()
Label(upper, text ="Sum:", font = ('raleway', 15), ).grid(row=0, column = 0)
Label(root, text ="Balance:", font = ('raleway', 15)).place(rely=1.0, relx=0, x=0, y=0, anchor=SW)
res = StringVar()
input1 = Entry(upper)
num2 = Entry(root)
result = Label(root, textvariable = res,font = ('raleway',13))
result.place(rely=1.0, relx=0, x=80, y=-2, anchor=SW)
input1.grid(row=0,column=2)
Button(upper, text ="Add Funds", command = bal).grid(row=4, column=2, ipadx = 65)
mainloop()
root.mainloop()
I tried to have a total that constantly updates in the function bal but it doesn't update for some reason. I am a python beginner, by the way :D
Thanks for your help!
In the bal() command function, all you need to do is retrieve the current input value and running total (balance), add them together, and then update the running total:
from tkinter import *
def bal():
ans = input1.get()
ans = int(ans) if ans else 0
tot = int(res.get())
tot = tot + ans
res.set(tot)
root = Tk()
root.geometry("1280x720")
upper = Frame(root)
upper.pack()
Label(upper, text="Sum:", font=('raleway', 15)).grid(row=0, column=0)
Label(root, text="Balance:", font=('raleway', 15)).place(rely=1.0, relx=0,
x=0, y=0, anchor=SW)
res = StringVar()
res.set(0) # initialize to zero
input1 = Entry(upper)
result = Label(root, textvariable=res, font=('raleway', 13))
result.place(rely=1.0, relx=0, x=80, y=-2, anchor=SW)
input1.grid(row=0,column=2)
Button(upper, text="Add Funds", command=bal).grid(row=4, column=2, ipadx=65)
root.mainloop()
You created a new IntVar and you are using .get on this. Instead you want to be use get on num2 to get the current number that is stored in there adding the input to this and updating the var.

Categories