How to pass a value to an entry validatecommand - python-3.x

I am validating a form in tkinter using validatecommand. There are two entries, one should only accept integers and the other floats.
Here is the basic code:
import tkinter as tk
class Form(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
validCmd1 = (self.register(self.val1), "%P")
validCmd2 = (self.register(self.val2), "%P")
self.lab1 = tk.Label(self, text = "Float:")
self.lab1.grid(row = 1, column = 0, padx = 10, pady = 10)
self.ent1 = tk.Entry(self, validate = "key", validatecommand = validCmd1)
self.ent1.grid(row = 1, column = 1)
self.lab2 = tk.Label(self, text = "Integer:")
self.lab2.grid(row = 2, column = 0, padx = 10, pady = (0,10))
self.ent2 = tk.Entry(self, validate = "key", validatecommand = validCmd2)
self.ent2.grid(row = 2, column = 1)
def val1(self, value):
try:
float(value)
except:
return False
else:
return True
def val2(self, value):
try:
int(value)
except:
return False
else:
return True
app = Form()
app.mainloop()
Instead of writing two different functions for two very similar tasks, I tried to pass an integer to the validate command (1 for float, 2 for integer). I tried to use lambdas to pass values to the validation functions, first on the self.register part: self.register(lambda: self.val1(1)) but this gave TypeError: <lambda>() takes 0 positional arguments but 1 was given and the second time I tried using lambda on the validatecommand command: validatecommand = lambda: validCmd1(1) which gives TypeError: 'tuple' object is not callable.Is it possible to pass values to a validation command?

The validatecommand option takes a tuple that can have any arguments you want. For example, if you want to pass an integer it's as easy as this:
validCmd1 = (self.register(self.val), 1, "%P")
validCmd2 = (self.register(self.val), 2, "%P")
def val(self, arg, value):
print(f"val1 arg='{arg}' value='{value}'")
...
Sometimes it's easier to understand if you separate the registration of the command from the value passed in to validatecommand. For example:
vcmd = self.register(self.val)
...
self.ent1 = tk.Entry(..., validatecommand = (vcmd, 1, "%P"))
self.ent2 = tk.Entry(..., validatecommand = (vcmd, 2, "%P"))

Related

How to pass arguments to lambda function in yscrollcommand in tkinter Listbox?

6 Listbox objects are generated in a for loop in app class.
And its yscrollcommand, 6 functions are hard coded.
(def function for 0, def function for 1...)
It seems if I can pass the index argument to lambda function,
6 functions for list scroll can be compressed to 1 for loop.
But the funtion in a class, it has argument 'self'.
It make me confusing.
How can I pass index argument to lambda function in yscrollcommand?
class app(tk.Frame):
def __init__(self):
self.root = tk.Tk()
self.root.title('title something')
# showing data frame
self.data_frame = tk.LabelFrame(self.root, text='')
self.data_frame.pack(fill='x')
self.scrollbar = tk.Scrollbar(self.data_frame)
self.scrollbar.pack(side='right', fill='y')
self.listboxes = []
self.listboxes_column = 6 # This can be vary.
# listboxes are in a list.
for i in range(self.listboxes_column):
self.listboxes.append(tk.Listbox(self.data_frame, selectmode='extended', height=20, width=0, yscrollcommand = self.scrollbar.set))
self.listboxes[i].pack(side='left')
# when self.listboxes_column == 3
# self.list_indexes == [[1, 2, 3], [0, 2, 3], [0, 1, 3], [0, 1, 2]]
self.list_indexes = []
for i in range(self.listboxes_column):
indexes = [j for j in range(self.listboxes_column)]
indexes.remove(i)
self.list_indexes.append(indexes)
# a listbox can scroll the others
# with lambda function and argument passing,
# I want to make these 6 lines be in a for loop.
# The from like this
# for i in range(6):
# self.listboxes[index_argument].config(yscrollcommand = lambda ????? : self.list_scrolls_all(index_argument ????? ))
self.listboxes[0].config(yscrollcommand = self.list0_scrolls_all)
self.listboxes[1].config(yscrollcommand = self.list1_scrolls_all)
self.listboxes[2].config(yscrollcommand = self.list2_scrolls_all)
self.listboxes[3].config(yscrollcommand = self.list3_scrolls_all)
self.listboxes[4].config(yscrollcommand = self.list4_scrolls_all)
self.listboxes[5].config(yscrollcommand = self.list4_scrolls_all)
self.scrollbar.config(command=self.bar_scrolls_all)
self.root.mainloop()
# functions for lists scroll from 0 to 5.
# I don't know how to pass argument via yscrollcommand in Listbox.
# I want a form like this.
#
# def list_scrolls_all(self, index, *args):
# for i in self.list_indexes[index] :
# self.listboxes[i].yview_moveto(args[0])
# self.scrollbar.set(*args)
def list0_scrolls_all(self, *args):
for i in self.list_indexes[0] :
self.listboxes[i].yview_moveto(args[0])
self.scrollbar.set(*args)
def list1_scrolls_all(self, *args):
for i in self.list_indexes[1] :
self.listboxes[i].yview_moveto(args[0])
self.scrollbar.set(*args)
# scroll bar
def bar_scrolls_all(self,*args):
for i in range(self.listboxes_column):
self.listboxes[i].yview(*args)
I just happen to be working on a multi listbox program so I've made a few modifications to your code.
I've included flexx function so all objects for totally flexible, although I had to replace pack the grid.
import tkinter as tk
def flexx(m, r = 0, c = 0, rw = 1, cw = 1):
if r != None:
m.rowconfigure(r, weight = rw)
if c != None:
m.columnconfigure(c, weight = cw)
class app:
def __init__(self):
self.root = tk.Tk()
self.root.title('title something')
# make root flexible
flexx(self.root)
# showing data frame
self.data_frame = tk.LabelFrame(self.root, text='Multiple Linked Listboxes')
self.data_frame.grid(row = 0, column = 0, sticky = tk.NSEW)
self.listboxes = []
self.listboxes_column = 6 # This can be vary.
self.scrollbar = tk.Scrollbar(self.data_frame)
self.scrollbar.grid(row = 0, column = self.listboxes_column, sticky = tk.NS)
# listboxes are in a list.
for i in range(self.listboxes_column):
self.listboxes.append(tk.Listbox(
self.data_frame, selectmode='extended',
height=20, width=0))
self.listboxes[i].grid(row = 0, column = i, sticky = tk.NSEW)
# make data_frame flexible
flexx(self.data_frame, c = i)
# populate listbox with some data for testing purposes
for b in dir(self):
self.listboxes[i].insert("end", b)
# connect yscollcommand to all listboxes
for a in self.listboxes:
a["yscrollcommand"] = self.move_to
if False: # Not sure waht this does
# when self.listboxes_column == 3
# self.list_indexes == [[1, 2, 3], [0, 2, 3], [0, 1, 3], [0, 1, 2]]
self.list_indexes = []
for i in range(self.listboxes_column):
indexes = [j for j in range(self.listboxes_column)]
indexes.remove(i)
self.list_indexes.append(indexes)
# connect scrollbar command to all y_views
self.scrollbar.config(command = self.y_view)
def y_view(self, *args):
for a in self.listboxes:
a.yview(*args)
def move_to(self, *args):
self.scrollbar.set(*args)
self.y_view("moveto", args[0])
if __name__ == "__main__":
demo = app()
demo.root.mainloop()

what am i doing wrong with tkinter application for email slicer?

making an email slicer,
some errors I'm getting are:
AttributeError: 'bool' object has no attribute 'index'
ValueError: substring not found
now, with this specific code, I'm getting no result at all, it just doesn't do anything when I click the button
root = Tk()
e = Entry(root)
e.grid(row = 6, column = 6)
s = Label(root)
s.grid(row = 1, column = 1)
wel = Label(root, text = "whats your email")
wel.grid(row = 1, column = 5)
inp = Entry(root)
inp.grid(row = 3, column = 5)
def callback(re = inp.get()):
us = re[:re.startswith("#")]
uss = re[re.startswith("#")+1:]
var = StringVar()
var.set(us + uss)
sub = Button(root, text = "submit", command = lambda:callback())
sub.grid(row = 5, column = 5)
final = Label(root, textvariable = var)
final.grid(row = 5, column = 6)
root.mainloop()
You need to
call inp.get() inside callback(), not as default value of argument
use find() instead of startswith()
call var.set(...) inside the function as well
def callback():
re = inp.get()
pos = re.find("#")
if pos >= 0:
user = re[:pos]
domain = re[pos+1:]
var.set(user+","+domain)
else:
var.set("Invalid email")
Note that above is not enough to check whether the input email is valid or not, for example two "#" in the input.

Changing a variable with a button (and a function) in Python Tkinter

I have the following code (it's partly in Dutch but I don't think that'll be an issue):
from tkinter import *
root = Tk()
woorden_en = ["mouse", "armadillo", "caterpillar", "buffalo", "dragonfly", "eel", "monkey", "lark", "manatee", "squid"]
woorden_nl = ["muis", "gordeldier", "rups", "buffel", "libelle", "paling", "aap", "leeuwerik", "zeekoe", "inktvis"]
nummer = IntVar()
nlWoord = StringVar()
enWoord = StringVar()
goedfout = StringVar()
def vorige():
nummer -= 1
def volgende():
nummer += 1
def controleer():
print("Correct!")
secondGrid = Frame(root)
secondGrid.grid(row = 2, column = 1, columnspan = 2)
labelVertaling = Label(root, text="vertaling")
textVertaling = Entry(root, width=30, textvariable = nlWoord)
runVorige = Button(secondGrid, text="vorige", command = vorige)
runVolgende = Button(secondGrid, text="volgende", command = volgende)
runControleer = Button(secondGrid, text="controleer", command = controleer)
labelWoord = Label(root, text="woord")
labelWoordEn = Label(root, textvariable = enWoord)
labelNo = Label(root, textvariable = nummer)
Correct = Label(root, textvariable = goedfout)
Correct.grid(row = 2, column = 0)
labelNo.grid(row = 0, column = 0)
labelWoord.grid(row = 0, column = 1)
labelWoordEn.grid(row = 1, column = 1)
labelVertaling.grid(row = 0, column = 2)
textVertaling.grid(row = 1, column = 2)
runVorige.grid(row = 0, column = 0, sticky = "W")
runVolgende.grid(row = 0, column = 1, sticky = "W")
runControleer.grid(row = 0, column = 2, sticky = "W")
nummer.set(1)
enWoord.set(woorden_en[0])
root.mainloop()
The start value of 'nummer' is 1, as set in the 3rd to last line. This value needs to be changed with either -1 or +1 when clicking buttons 'vorige' (previous) or 'volgende' (next). The current code in the functions give me erros. Apparently I need to use set/get functions, but I cannot find out how to make it work. Any input or help would be appreciated.
Just change your function to:
def vorige():
nummer_val = nummer.get()
nummer_val -= 1
nummer.set(nummer_val)
def volgende():
nummer_val = nummer.get()
nummer_val += 1
nummer.set(nummer_val)
This is because nummer is an IntVar() and you have to get the value of the variable using get() method. After that you have to assign it to a variable and then reduce/increase its value by 1. The first error you were getting was because the nummer was not globalized inside the function, but that was not the approach you should have taken, anyway this should fix your errors.

Converting IntVar() value to Int

Iam trying to convert Intvar() value to Int by
self.var1 = IntVar()
self.scale = ttk.LabeledScale(self.frame1, from_ = 3, to = 7, variable = self.var1).grid(row = 2, sticky = 'w')
value = int(self.var1)
but got an error saying
TypeError: int() argument must be a string, a bytes-like object or a number, not 'IntVar'
You need to invoke the .get method of IntVar which returns the object's value as an integer.
See inline comment for info.
self.var1 = IntVar()
self.scale = ttk.LabeledScale(self.frame1, from_ = 3, to = 7, variable =
self.var1).grid(row = 2, sticky = 'w')
value = self.var1.get() #the get() method of the IntVar object will return an int value
Hope this will help you!

updating tkinter window for text background changes

Trying to show a green or red background in the text field of the answer to the simple addition quizzer.
Currently in PyCHarm complains that:
Entry.grid_configure(background = "red")
TypeError: grid_configure() missing 1 required positional argument: 'self'
0
I can't seem to figure this out. Any help is appreciated.
Here's the code so far:
from tkinter import *
import random
class MainGUI:
def __init__(self):
window = Tk() # Create the window
window.title("Addition Quizzer") # Set the title
#window.width(len(window.title()))
self.number1 = random.randint(0, 9)
self.number2 = random.randint(0, 9)
Label(window, text = "+").grid(row = 2, column = 1, sticky = E)
Label(window, text = "Answer").grid(row = 3, column = 1, sticky = W)
self.firstNumber = StringVar()
Label(window, text = self.number1, justify = RIGHT).grid(row = 1, column = 2)
self.secondNumber = StringVar()
Label(window, text = self.number2, justify = RIGHT).grid(row = 2, column = 2)
self.entry = StringVar()
Entry(window, textvariable = self.entry, justify = CENTER, width = 4, background = "grey").grid(row = 3, column = 2)
Button(window, text = "Answer:", command = self.computeAnswer).grid(row = 4, column = 1, sticky = E)
self.result = StringVar()
Label(window, textvariable = self.result).grid(row = 4, column = 2)
window.mainloop() # Create the event loop
def computeAnswer(self):
self.result.set(format(self.number1 + self.number2))
if self.entry == self.result:
self.displayCorrect()
else:
self.displayIncorrect()
def displayCorrect(self):
# self.correctAnswer = "Correct"
# Label(self.window, text = self.correctAnswer, background = "green", justify = RIGHT).grid(row = 5, column = 2)
Entry.grid_configure(background = "green")
def displayIncorrect(self):
# self.incorrectAnswer = "Incorrect"
# Label(self.window, text = self.incorrectAnswer, background = "red", justify = RIGHT).grid(row = 5, column = 2)
Entry.grid_configure(background = "red")
MainGUI()
If you had read and followed this in the Help Center material, you would have reduced your code to the following, which still gets the same error message.
from tkinter import *
Entry.grid_configure()
The message refers to the fact that Python instance methods require an instance. This is usually done by calling the method on an instance instead of the class. Otherwise, an instance must be given as the first argument. Consider
mylist = []
mylist.append(1)
list.append(mylist, 2)
print(mylist)
# [1, 2]
You need to save a reference to your Entry box. Change
Entry(window, ..., background = "grey").grid(...)
to
self.entry = Entry(window, ..., background = "grey").grid(...)
I do not know if calling .grid_configure(background=color will do what you want.
This will, I am sure.
self.entry['background'] = 'red'

Resources