So if I want to restrict a value to be between 100 and 200...
import tkinter as tk
master = tk.Tk()
def validatePLZ(index, value_if_allowed):
print("validatePLZ with index:%s and proposed value:%s" % (index, value_if_allowed))
if int(index) > 3 or len(value_if_allowed) > 3:
return False
print("Not too long")
value = int(value_if_allowed)
print("Considering value: ", value)
if value < 100 or value > 200:
return False
return True
vcmd = (master.register(validatePLZ), '%i', '%P')
ent = tk.Entry(master, validate="key", validatecommand=vcmd)
ent.insert(0, "150")
ent.pack()
tk.Button(master, text="Validate").pack()
tk.mainloop()
This has worked in less strict conditions, where zero was allowed, but if the value is '150' and I am trying to change to '170' by highlighting '5' and typing '7' what I see is:
Deletion of '5' with a result of '10'
Addition of '7', after failed deletion, with a result of '1570'
But it fails on #1 so it is not working. I do still want #1 to happen if just deleting a character, but how can the highlight-replace be combined into one check? Or how do I know if it is a highlight-replace and not simply a deletion?
Related
So basically, I am trying to create a python program to detect whether the 3 digit number entered by the user is an Armstrong number or not. It's working fine in python, except it's printing the answer 3 times in terminal. But when I use tkinter, I get some problems since I basically just got to know by it just a few hours ago and don't really know how to use it. The problem I get is in > operator, I'm receiving data in Entry() but > operator is for integers so it is giving me type errors.
Here's the error
TypeError: '>' not supported between instances of 'int' and 'Entry'
Here is my code
import re
from tkinter import *
root = Tk()
label = Label(root, text="Enter a 3 digit Number")
label.pack()
num = Entry(root, width=50, bg="orangered")
num.pack()
def myclick():
Sum = 0
temp = num
if 1000 > num > 99:
while temp > 0:
digit = temp % 10
Sum += digit ** 3
temp //= 10
if num == Sum:
label2 = Label(root, num, "is an Armstrong number")
label2.pack()
else:
label3 = Label(root, num, "is not an Armstrong number")
label3.pack()
else:
label4 = Label(root,
"The number you entered is not a 3 digit number, Please Enter a number between 100 and 999")
label4.pack()
MyButton = Button(root, text="Click", command=myclick)
MyButton.pack()
root.mainloop()
All I want is to make this program work in a gui, and stop printing the result thrice. Can anyone help me?
As num is an Entry widget, you cannot use it directly in value comparison. Use int(num.get()) to convert the content to an integer number. You also need to cater invalid input, for example something like "abc", using try / except.
Also you create new label (for showing the result) in each iteration of the while loop, that is why you get 3 labels for each checking. It is better to create the result label once outside myclick() and update it inside the function instead.
Below is the modified myclick():
def myclick():
try:
number = num.get()
value = int(number)
if 999 >= value >= 100:
total = sum(int(digit)**3 for digit in number)
message = f"{value} {'is' if value == total else 'is not'} an Armstrong number"
else:
message = "Enter a number between 100 and 999"
except ValueError:
message = f"'{number}' is not a valid number"
result.config(text=message) # show the result
And create result label outside the function:
result = Label(root)
result.pack()
I have split the error handing into 3 parts, ( check_black, raise_error, delete_error)
About about the functions:
the check_blank() function checks if there are any errors in the input
entries and raise_error() function raises the error by gridding() a
ErrorLabel. and if the error have been resolved then delete_error()
function deletes the error label.
(there are 5 entry boxes and 4 check buttons, and a 'done' button)
( error should be raised if there's no input for all 5 Entries or at least one check button hasn't been checked )
this is the check_blanks() function:
def checkblanks(self):
flag = 0
if not self.Text1.get(): #text.get() and Answer1.get() etc stores input from entry so if there's no entry then it will be empty
flag = 1
if not self.Answer1.get():
flag = 1
if not self.Answer2.get():
flag = 1
if not self.Answer3.get():
flag = 1
if not self.Answer4.get():
flag = 1
if not self.var.get(): # var.get stores input from check button so if its not checked then there won't be anything stored in var.get()
flag += 2
if flag == 0: # flag = 0 means no error
self.ErrorLabel = None
self.add() #### EDIT ####
self.delete_error()
elif flag == 1 or flag == 2 or flag == 3: # if flag is 1 or 2 or 3 means there is error
self.raise_error(flag)
flag 0 = means no errors
flag 1 = means that there's no input in all 5 entries
flag 2 = means at least one check button hasn't been checked
flag 3 = means both above errors occurred
this is the raise error_function:
def raise_errors(self, flag):
if flag == 1 or flag == 3:
self.L5.grid_forget()
self.ErrorLabel = tk.Label(self.frame2, text="Error: Fill all the blanks !", fg='white', bg='red')
self.ErrorLabel.grid(row=7, column=0, pady=10)
elif flag == 2:
self.L5.grid_forget()
self.ErrorLabel = tk.Label(self.frame2, text="Error: check ✔️ the correct answer ", fg='white', bg='red')
self.ErrorLabel.grid(row=7, column=0, pady=10)
and the delete_error function:
def delete_error(self):
if self.ErrorLabel is not None: # checks if the error label exists
self.ErrorLabel.grid_forget()
but there is a problem:
When the error is raised and then error label is made; after the
errors are resolved then the error label is not deleted by
delete_error()
and sometimes multiple error labels overlap each other and it not very
efficient
I need to make the error handling functions more simple and work
properly.
EDIT:
also the function add() is called if flag == 0,( if there's no errors
) the add() function just creates a new frame
with the same entries and check buttons its and checks for errors all
over again.
its just a repeat loop
this is that function:
def add(self):
self.X += 1
self.frame2.grid_forget() # deleted main frame
self.amount_of_questions -= 1
self.question(self.X) # question() creates new frame with same widgets
It is over writing each label because you are creating new label for each function call and so on, so I think its safe for us now, to get rid of None and try to use config:
def checkblanks(self):
flag = 0
.....
self.ErrorLabel = tk.Label(self.frame2,fg='white',bg='red') # Define the variable with basic base options
if flag == 0:
self.delete_error()
elif flag == 1 or flag == 2 or flag == 3:
self.raise_error(flag)
and then delete_error() would be:
def delete_error(self):
self.ErrorLabel.grid_forget() # Now always errorlabel is defined, you might want to use winfo_exists
So now you have to use config to update the widget inside raise_errors():
def raise_errors(self, flag):
if flag == 1 or flag == 3:
self.L5.grid_forget()
self.ErrorLabel.config(text="Error: Fill all the blanks !") # Update the widget
self.ErrorLabel.grid(row=7, column=0, pady=10) # Grid it
elif flag == 2:
self.L5.grid_forget()
self.ErrorLabel.config(text="Error: check ✔️ the correct answer ") # Update the widget
self.ErrorLabel.grid(row=7, column=0, pady=10) # Grid it
Since I don't have a code to test this up with, this is based on imagination, I think you need winfo_exists inside delete_errors() too.
after the errors are resolved then the error label is not deleted by delete_error()
This might be due to the way self.ErrorLabel was defined as None. Now that it is always defined as a Label, it should be fine.
EDIT: Can you try moving the label inside of __init__:
def __init__(self):
....
self.ErrorLabel = tk.Label(self.frame2,fg='white',bg='red')
Starting my adventure with python Tkinter I just tried to develop an Entry which accepts only dates. However, more than just validate the Entry I was trying to coordinate the content "appearance" by adding "/" in proper positions to have a valid date format (dd/mm/aaaa). The problem is: if I insert a char instead of another, using the Entry method or StringVar, the validate function just work no longer for any other insert in the Entry.
What is the problem with that solution?
Is there a way to make it work?
Here is the code I was developing:
from tkinter import *
root = Tk()
def f(att):
print(att)
if len(att) == 0:
return True
elif len(att) == 1 and att in "123":
return True
elif len(att) == 2 and att.isdigit() and int(att) < 32:
return True
elif len(att) == 3 and att[-1] != "/":
ent.insert(END, "/")
return True
else:
return False
ent = Entry(root, validate="key")
ent["validatecommand"] = (ent.register(f), "%P")
ent.pack(pady=10, padx=10)
ent.focus()
root.mainloop()
I wrote some simple code to describe my problem: I want to take an integer value from an entry to use it later.I tried also to use a spin box. here is my code:
from tkinter import*
win=Tk()
win.geometry('300x200')
e=Entry(width=10)
e.pack()
y=int(e.get())
print(y*2)
I always get the same error:
y = int(e.get())
ValueError: invalid literal for int() with base 10 ' '
I don't know why this is happening!
ValueError: invalid literal for int() with base 10 ' '
means that you are trying to convert the string "" to an integer. This, of course, is invalid. The reason you are trying to convert an empty string to an integer is that you are not allowing any value to be placed into the entry.
A good way to allow this to happen would be using a button, which calls a function to get the value within the entry and print it. Also, you are missing the line win.mainloop() which you would need at the end of your code.
Here is the example code you are probably asking for:
from tkinter import *
win = Tk()
win.geometry('300x200')
def printVal():
y = int(e.get())
print(y*2)
e = Entry(width=10)
e.pack()
b = Button(text="push me to print the value in e")
b.pack()
win.mainloop()
This code will still return errors if the value in the entry is not a valid integer, so if you want it to be robust, you'll have to play around with it.
there is several problems in your example, for example. in which moment you are trying to get value from Entry?
your code should looks something like:
from tkinter import *
def validate(value):
print (value)
try:
if value:
return int(value)
except:
return None
def calculate():
x = e1.get()
x_validate = validate(x)
if x_validate == None:
e1.delete(0, END)
e1.insert(0, 'you need to enter integer')
Label1.config(text='')
else:
result = x_validate*2
Label1.config(text=result)
win = Tk()
e1 = Entry(win)
e1.grid(row=0, column=0)
Button(win,text='Calculate', command = calculate).grid(row=0, column=1)
Label(win,text='Result').grid(row=1, column=0)
Label1 = Label(win)
Label1.grid(row=1, column=1)
win.mainloop()
Example if you enter integer and press calculate
Example if you enter string and press calculate
I'm working on a program regarding math equations, so in my program there are tons of entries but I'm having difficulties clearing them.
For the entries I'm restricting both a character limit and I only allow number and (",") or ("."). If I type for example ' 1000 ' in my entry, then highlight it and press backspace everything works out. But as soon as I type ' 100,25 ' and add a comma into the mix. It wont delete after pressing backspace.
from tkinter import *
root = Tk()
def validatecontent(var):
return (var.isdigit() == bool(var)) or var == (",") or var == (".")
vcmd = (root.register(validatecontent), '%S')
def character_limit6(var):
if len(var.get()) > 0:
var.set(var.get()[:6])
var = StringVar()
entry = Entry(root, textvariable=var, validate='all',
validatecommand=vcmd)
entry.pack()
var.trace("w", lambda *args: character_limit6(var))
root.mainloop()
Edit: It seems that the problem is that it refuses to recognize a string of "," or ".", alternatively a string of numbers and "," / "." as legitimate. The following seems to work:
from tkinter import *
root = Tk()
var = StringVar()
var.trace("w", lambda *args: character_limit6(var))
def validatecontent(var):
return var.isdigit() == bool(var) or "," in var or "." in var
def character_limit6(var):
if len(var.get()) > 0:
var.set(var.get()[:6])
vcmd = (root.register(validatecontent), '%S')
entry = Entry(root, textvariable=var, validate='all',
validatecommand=vcmd)
entry.pack()
root.mainloop()
In the question when you delete a selection the var parameter in validatecontent is a string eg. '0,0' which fails your validation routine.
Do you want to accept only valid float strings?
Are you expecting strings like '1,234.45' or '123,45'?
Below I've assumed '123,45'
Validatecontent tries to convert the string to a float after replacing any ',' with '.'.
If it can it returns true otherwise it returns True only if the string is empty.
from tkinter import *
root = Tk()
def validatecontent(var): # Amended var is now the complete string.
try:
temp = var.replace(',', '.') # Replace ','' with '.' for float conversion assumes , is decimal point.
# temp = var.replace(',',"") # Or replace , with empty if , is a thousands delimiter.
fl = float(temp)
# if temp converts to a float return length of string is less than 7
return len(var) < 7
except ValueError:
return var == "" # If it doesn't convert to a float only accept an empty string.
vcmd = (root.register(validatecontent), '%P') # '%P' passes the whole new string to validate content.
entry = Entry(root, validate='key', validatecommand=vcmd) # Validate on key only.
entry.pack()
root.mainloop()
There may well be better validation functions involving regular expressions. This is reasonably easy to follow though. The entry can still be linked to a StringVar if required. It's not required to do the validation though.