making tkinter entry box value into a float (python) - python

I am a beginner. I have tried everything to make the following code take numeric inputs into entry boxes and do a calculation with them. I am getting the ValueError and nothing I do makes that stop happening. This is supposed to be a program that calculates monthly interest payments and a total paid out. I am keeping it at a simple product until I fix this much more basic problem. Thanks.
def multiply(var1, var2, var3):
product = float(var1 * var2 * var3)
return product
def btnClick(event):
x = float(entry.get())
main = Tk()
main.title("Assignment 16")
main.geometry("500x500")
main["bg"] = "#000066"
lblFirst = Label(main, text="Amount to Pay: ")
lblFirst.grid(row=0, column=3, pady=5)
entry = Entry(main, width=20)
entry.grid(row=0, column=4)
amount = entry.get()
lblSecond = Label(main, text="Interest Rate (like 7.5): ")
lblSecond.grid(row=2, column=3, pady=10)
entry2 = Entry(main, width=20)
entry2.grid(row=2, column=4)
rate = entry2.get()
lblThird = Label(main, text="Years to Pay: ")
lblThird.grid(row=4, column=3, pady=15)
entry3 = Entry(main, width=20)
entry3.grid(row=4, column=4)
years = entry3.get()
try:
# Try to make it a float
if amount.isnumeric():
amount = float(amount)
except ValueError:
# Print this if the input cannot be made a float
print("Bad input")
try:
# Try to make it a float
if rate.isnumeric():
rate = float(rate)
except ValueError:
# Print this if the input cannot be made a float
print("Bad input")
try:
# Try to make it a float
if years.isnumeric():
years = int(years)
except ValueError:
# Print this if the input cannot be made a float
print("Bad input")
lblFourth = Label(main, text="Monthly Payment: ")
lblFourth.grid(row=6, column=3, pady=15)
lblFourthTwo = Label(main, text="XXXXX")
lblFourthTwo.grid(row=6, column=4)
lblFifth = Label(main, text="Total of Paymenta: ")
lblFifth.grid(row=8, column=3, pady=15)
lblFifthTwo = Label(main, text="XXXXX")
lblFifthTwo.grid(row=8, column=4)
button1 = Button(main, text='Convert', width=10, command=btnClick)
button2 = Button(main, text='Calculate', width=10, command=multiply(amount, rate, years))
button1.grid(padx=20, pady=20)
main.mainloop()

All your code runs before the mainloop starts.
Programs using GUI-toolkits like tkinker are event-driven. Your code only runs in the set-up before the mainloop and after that in event-handlers.
You can use validation to ensure that only numbers are entered.
Working example (for Python 3) below. This also shows how to get the value from an editbox in an event handler and how to create synthetic events to update other widgets.
import tkinter as tk
from tkinter import ttk
# Creating and placing the widgets
root = tk.Tk()
root.wm_title('floating point entry')
qedit = ttk.Entry(root, justify='right')
qedit.insert(0, '100')
qedit.grid(row=0, column=0, sticky='ew')
result = ttk.Label(root, text='100')
result.grid(row=1, column=0)
ttk.Button(root, text="Exit", command=root.quit).grid(row=2, column=0)
# Callback functions
def is_number(data):
if data == '':
return True
try:
float(data)
print('value:', data)
except ValueError:
return False
result.event_generate('<<UpdateNeeded>>', when='tail')
return True
def do_update(event):
w = event.widget
number = float(qedit.get())
w['text'] = '{}'.format(number)
# The following settings can only be done after both the
# widgets and callbacks have been created.
vcmd = root.register(is_number)
qedit['validate'] = 'key'
qedit['validatecommand'] = (vcmd, '%P')
result.bind('<<UpdateNeeded>>', do_update)
# Run the event loop.
root.mainloop()

Related

How to set time.sleep for different widgets

There is code that checks the availability of sites.
How can we make the status for each site to change at a user defined time (i.e. the time for each site can be different) The problem is that the number of sites is not limited, because you can add more lines to the application, so I do not understand how to implement it.
I attach a picture of how it looks like:
Code:
import tkinter as tk
from tkinter import ttk
import requests
import time
from tkinter import *
from tkinter import messagebox
data_list = []
window = Tk()
window.geometry('400x700')
window.title("SiteChecker")
def set_input(obj, value):
obj.delete(1.0, "END")
obj.insert("END", value)
def SiteCheck():
# time.sleep
for data in data_list:
url = data[0].get()
status = data[2]
if not str(url).startswith('http'):
continue
print(url)
Get_Response = None
try:
Get_Response = requests.get(url)
except:
status.config(text='status bad')
continue
if Get_Response.status_code == 200:
status.config(text='status ok')
pass
implement
else:
status.config(text='status bad')
def clicked():
txt = Entry(window, width=18)
txt.grid(column=0, pady=8)
txt_row = txt.grid_info()['row']
tim = Entry(window, width=3)
tim.grid(row=txt_row, column=1, pady=8)
txt_row = tim.grid_info()['row']
result1 = Label(window, text="status")
result1.grid(row=txt_row, column=2, pady=8)
data_list.append([txt, tim, result1])
lbl1 = Label(window, text="Enter references:")
lbl1.grid(column=0, row=1)
lbl2 = Label(window, text="Enter the test time: ")
lbl2.grid(column=1, row=1)
lbl3 = Label(window, text="Availability status ")
lbl3.grid(column=2, row=1)
for loop in range(2, 6):
txt1 = Entry(window, width=18)
txt1.grid(column=0, row=loop, pady=8)
tim1 = Entry(window, width=3)
tim1.grid(column=1, row=loop, pady=8)
result1 = Label(window, text="status")
result1.grid(column=2, row=loop, pady=8)
data_list.append([txt1, tim1, result1])
btn = Button(window, text="Add another site", command=clicked)
btn.grid(column=1, row=0)
Check_Button = Button(
window,
command=SiteCheck,
text='Start checking',
)
Check_Button.grid(row=0, column=2)
window.mainloop()
See here (How do I make my function run every second) how to use tkinter after() to achieve what you want. time.sleep won't work for your purpose because it is blocking the execution of the entire code.
From the link given in the comment to your question by Patrick Artner :
Tkinter root windows have a method called after() which can be used to schedule a function to be called after a given period of time. If that function itself calls after() you've set up an automatically recurring event.

How to avoid TclError if I want to update the results obtained from entries instantly?

I want to add two numbers and add them and update the result instantly without using a button.
I have written this code using after() to do that.
from tkinter import *
root = Tk()
root.geometry("400x400")
label1 = Label(root, text="1st number:")
label1.grid(row=0, column=0)
label2 = Label(root, text="2nd number:")
label2.grid(row=1, column=0)
first_no = IntVar()
second_no = IntVar()
entry1 = Entry(root, textvariable=first_no)
entry1.grid(row=0, column=1)
entry2 = Entry(root, textvariable=second_no)
entry2.grid(row=1, column=1)
def auto():
try:
S = first_no.get() + second_no.get()
label3.config(text="Sum: " + str(S))
# call again after 100 ms
root.after(100, auto)
except TclError:
print("there is an Error")
label3 = Label(root)
label3.grid(row=3, column=1)
auto()
root.mainloop()
The code works well. However, if we delete the numbers in the entries using the delete or backspace keyboards this error raises: _tkinter.TclError: expected floating-point number but got "", and if I enter new values the code does not work anymore.
I wondered to know how I avoid this problem??
I found the solution. I added another root.after(100, auto) under exception TclError

Solving problem to generate custom number from inserted values with GUI in Python

Hello im having python learning project. I want to insert in GUI two numbers, which are defining range for program to generate random number from.
I am really having problems with calling function with press of the button. And constantly getting error ValueError: invalid literal for int() with base 10: '', when trying to convert string from entry in GUI to int and then inserting them into random.randint.
Thx for Help!
from tkinter import *
import random
root = Tk()
root.title("Generator custom random number")
#function that gets number from entry converts string to int and defines target number in stevilo
def function():
string_answer1 = prvo_stevilo.get()
int1 = int(string_answer1)
string_answer2 = drugo_stevilo.get()
int2 = int(string_answer2)
stevilo = random.randint(int1, int2)
#Defining GUI
navodilo = Label(root, text="Choose custom lower and upper number to chose random number from", width=60)
navodilo2 = Label(root, text="From", width=20, borderwidth=3)
navodilo3 = Label(root, text="To", width=20, borderwidth=3)
prvo_stevilo = Entry(root, width=20, borderwidth=3)
drugo_stevilo = Entry(root, width=20, borderwidth=3)
gumb = Button(root, text="Generate number", width=17, height=2, command=function)
praznavrstica = Label(root, text="")
izpis = Label(root, text="Random number is: ", width=20)
izpis_stevila = Label(root, text="" + stevilo)
#Showcase of gui
navodilo.grid(row=0, column=0, columnspan=1)
navodilo2.grid(row=1, column=0, columnspan=1)
navodilo3.grid(row=3, column=0, columnspan=1)
prvo_stevilo.grid(row=2, column=0, columnspan=1)
drugo_stevilo.grid(row=4, column=0, columnspan=1)
praznavrstica.grid(row=5, column=0, columnspan=1)
gumb.grid(row=6, column=0, columnspan=1)
praznavrstica.grid(row=7, column=0, columnspan=1)
izpis.grid(row=8, column=0, columnspan=1)
izpis_stevila.grid(row=9, column=0, columnspan=1)
#Loop
root.mainloop()
I've noticed few problems with your code. I was able to make it running without too many changes, although probably it is not the best way.
First answer to your question: you are getting this error, because you are trying to change string -> '' to int. It happens because function() is running before you click button.
Another problem:
izpis_stevila = Label(root, text="" + stevilo)
variable 'stevilo' simply doesn't exist before calling function(), so delete it from here.
My proposition for changes:
1)
gumb = Button(root, text="Generate number", width=17, height=2,command = lambda: function())
Without lambda your function will run no matter of state of your button.
2)
first = IntVar(root, value=0)
second = IntVar(root, value=1)
prvo_stevilo = Entry(root, width=20, borderwidth=3, textvariable=first)
drugo_stevilo = Entry(root, width=20, borderwidth=3, textvariable=second)
If you run function without any values in your entry you will get error. This change allows you to put default value for your entry widgets.
3)
def function():
if prvo_stevilo.get() == '' or drugo_stevilo.get() =='':
return
else:
string_answer1 = prvo_stevilo.get()
int1 = int(string_answer1)
string_answer2 = drugo_stevilo.get()
int2 = int(string_answer2)
stevilo = random.randint(int1, int2)
izpis_stevila = Label(root, text=str(stevilo))
izpis_stevila.grid(row=9, column=0)
Firstly check if your entry is not empty.
Secondly update label, also remeber about changing int to string here text=str(stevilo).

How to Get Iterations to Update with addition to list

So I've been struggling with an issue for a week or so, been googling around trying to find different solutions, etc and getting nowhere. I was advised to put functioning code on here so I've cut it down some while still showing the issue.
I want to have a main page listing a set of goals, then if you click on the "Goal Entry" button up top a new window opens where you can input additional goals. Then you type in your desired additions, hit enter, and it adds it to the list on the main page.
I've accomplished all of the above EXCEPT, after you add the goals (and I have the list printing before and after so I know they're being added) and the entry window closes, the list of labels (created by an iteration) hasn't updated accordingly.
How do I get the list on the main page to automatically update when a new item is added to the list?
from tkinter import *
pg = ["goal1","goal2"]
pgtotal=1
psum=len(pg)
class yeargoals():
global pg, hg, fg, rg, rgtotal
def __init__(self,master):
self.master = master
master.title("This Year's Goals")
self.buttonframe = Frame(root)
self.buttonframe.pack(side=TOP, padx = 150, fill=BOTH)
self.home = Button(self.buttonframe, text="Home Page")
self.home.grid(row=1, column=1, padx=10)
self.enter = Button(self.buttonframe, text="Goal Entry", command=self.winenter)
self.enter.grid(row=1, column=2, padx=10)
self.finalize = Button(self.buttonframe, text="Finalize for Year")
self.finalize.grid(row=1, column=3, padx=10)
self.dashboard = Button(self.buttonframe, text="Goal Dashboard")
self.dashboard.grid(row=1,column=4, padx=10)
self.goalframe = Frame(root)
self.goalframe.pack(side=TOP, padx=150, pady=50, fill=BOTH, expand = True)
#Makes the label Fram I want the Checkboxes to go in
self.LabelFramep= LabelFrame(self.goalframe,text="Professional Goals")
self.LabelFramep.pack(side=LEFT, padx=10, anchor = N, fill=BOTH, expand = True)
#Makes the from the list above
for goal in pg:
l = Checkbutton(self.LabelFramep, text=goal, variable=Variable())
l.config(font=("Courier",12))
l.grid(sticky=W)
self.ptotal=Label(self.LabelFramep,text="Progress so far: "+str(pgtotal)+"/"+str(psum))
self.ptotal.config(font=("Courier",12))
self.ptotal.grid(sticky=W)
self.pper=Label(self.LabelFramep, text=str(round((pgtotal/psum)*100))+"% Complete")
self.pper.config(font=("Courier",12))
self.pper.grid(sticky=W)
def winenter(self):
global pg
self.winenter = Toplevel(root)
options = ["Professional", "Health", "Financial", "Reward Items"]
variable = StringVar(self.winenter)
variable.set(options[0])
#Title of entry section
t1 = Label(self.winenter, text="New Goal Entry")
t1.grid(row=0, column=1, columnspan=2)
#dropdown menu
d = OptionMenu(self.winenter, variable, *options)
d.grid(row=1, column=2)
#entry fields
e1 = Entry(self.winenter)
e1.grid(row=2, column=2, padx = 10, pady=5)
e2 = Entry(self.winenter)
e2.grid(row=3, column=2, padx=10, pady=5)
e3 = Entry(self.winenter)
e3.grid(row=4, column=2, padx=10, pady=5)
e4 = Entry(self.winenter)
e4.grid(row=5, column=2, padx=10, pady=5)
e5 = Entry(self.winenter)
e5.grid(row=6, column=2, padx=10, pady=5)
#Label for entry fields
l1 = Label(self.winenter, text="Goal Number 1")
l1.grid(row=2, column=1)
l2 = Label(self.winenter, text="Goal Number 2")
l2.grid(row=3, column=1)
l3 = Label(self.winenter, text="Goal Number 3")
l3.grid(row=4, column=1)
l4 = Label(self.winenter, text="Goal Number 4")
l4.grid(row=5, column=1)
l5 = Label(self.winenter, text="Goal Number 5")
l5.grid(row=6, column=1)
def enter():
global pg, main
print (pg)
if variable.get() == "Professional":
pg.append(e1.get())
self.winenter.destroy()
print (pg)
#Goal entry execute button
b = Button(self.winenter, text="Enter Goals", command=enter)
b.grid(row=7, column = 1)
root = Tk()
Window = yeargoals(root)
root.mainloop()
In your callback function to button "Enter Goals", you have done nothing to update your main window. Maybe you think the main window will magically keep updated with the variable pg, no, you need to do all those updates manually in your callback function.
For example, change your callback enter() to:
def enter():
global pg, main
print (pg)
if variable.get() == "Professional":
pg.append(e1.get())
l = Checkbutton(self.LabelFramep, text=pg[-1], variable=Variable())
l.config(font=("Courier",12))
l.grid(sticky=W)
self.winenter.destroy()
print (pg)
You can find the main window is updated after you click "Enter Goals".

How to make label and entry start blank

Any idea on how to make all the entry and labels in my GUI start blank but then update when the calculate function happens? They currently start with a 0. I have tried many things but nothing has worked.
Here is code:
from tkinter import *
root = Tk(className="Page Calculator")
root.title("PgCalc")
read = IntVar()
total = IntVar()
left = IntVar()
percent = IntVar()
def calculate(event=None):
try:
left.set(total.get() - read.get())
percent.set(int(read.get()*100/total.get()))
except ZeroDivisionError:
print("ZeroDivisionError")
else:
print()
root.bind('<Return>', calculate)
read_label = Label(root,text="Pages Read:")
read_label.grid(column=1, row=1)
read_entry = Entry(root, width=8, textvariable=read)
read_entry.grid(column=2, row=1)
read_entry.focus()
total_label = Label(root,text="Total Pages:")
total_label.grid(column=1, row=2)
total_entry = Entry(root, width=8, textvariable=total)
total_entry.grid(column=2, row=2)
calculate_button = Button(root,text="Calculate",command= calculate)
calculate_button.grid(column=2, row=3)
percent_label = Label(root,text="Percent Finished:")
percent_label.grid(column=1, row=4)
left_label = Label(root,text="Pages Left:")
left_label.grid(column=1, row=5)
percentresult_label = Label(root,textvariable=percent)
percentresult_label.grid(column=2, row=4)
leftresult_label = Label(root,textvariable=left)
leftresult_label.grid(column=2, row=5)
root.mainloop()
IntVar() has a default value of 0. Even though they are IntVar, you can set strings as their value (note that when you try to get its value, you'll get an error if they still contain strings).
So you can simply do
read = IntVar()
read.set("")
But, since you are using Entry, you don't need any IntVar at all. You can directly get entry's value and cast it to an integer.
def calculate(event=None):
try:
leftresult_label.config(text=str(int(total_entry.get()) - int(read_entry.get())))
except ValueError:
print("Please enter a number")
#You need to remove textvariables from entries as well like below
total_entry = Entry(root, width=8)

Categories

Resources