Python - User input - start function automatically after entering 7 digits - python

I am struggling to find the correct search terms to find an answer to my question. I have a program that is working, but I am trying to minimize the number of clicks required to make it work. I have a tkinter interface that takes a 7 digit input and looks it up in a spreadsheet and then displays some relevant information. Right now, the user scans a barcode which puts 7 digits in an entry box like this:
workorder = tk.Entry(root)
workorder.focus()
canvas1.create_window(175, 800, window=workorder)
Then, the user has to press the enter key to start the function that uses those 7 digits:
def f1(event):
getPartNumber()
root.bind('<Return>', f1)
I am trying to find a way to start the function automatically so a keyboard is not required. I was trying to do something like:
if len(str(workorder)) == 7:
getPartNumber()
However, it seems like this only works to check the length of an entry after the entry has been made. Is it possible to check on the state of the entry box before the entry is made?

You can associate a variable with the entry widget, and then set a trace on the variable. The trace will call a function whenever the data in the widget changes. Inside the trace you can examine the length of the data and call a function when the length is 7.
Here's an example that updates a label when you enter 7 characters:
import tkinter as tk
root = tk.Tk()
var = tk.StringVar()
entry = tk.Entry(root, textvariable=var)
label = tk.Label(root, text="", anchor="w")
entry.pack(side="top", fill="x")
label.pack(side="top", fill="x")
def boom(*args):
if len(var.get()) == 7:
label.configure(text="Boom!")
var.trace("w", boom)
root.mainloop()

Related

How do you change the output to lowercase in Tkinter?

I'm using Tkinter in python for the first time.
I'm trying to make the output the user has entered lowercase.
So if the enter "TEST" the output would be "test"
My code is:
from tkinter import *
# event functions (10:30 mins)
def onClickSubmitButton(): # submit button event handler
userEnteredText = textbox.get() # get the enterd text from the Entry text box
outputTextField.delete(0.0, END) # (14:30 mins)delete all of the output field text contents
outputTextField.insert(END, userEnteredText) # (15:50 mins) output the user eneterd text
#main window (1:30 mins)
window = Tk()
window.title("Python Glossary")
window.configure(background="white")
# display text using - Label (05:30 mins)
Label(window, text="Enter a string and press submit", bg="white", font="none 12 bold").grid(row=1,column=0,sticky=W) # using grid layout
# textbox for text entry - Entry (8:15 mins)
textbox = Entry(window, width=20, bg="white")
textbox.grid(row=2, column=0,sticky=W) # grid position of textbox
# submit button - Button (9:30 mins) - calls onClickSubmitButton function when clicked
Button(window, text="SUBMIT", width=6, command=onClickSubmitButton ).grid (row=2,column=1, sticky =W)
#definitions - Label (11:50 mins)
Label(window, text="\n Your string", bg="white", font="none 12 bold").grid(row=4,column=0,sticky=W) # using grid layout
# output textField - Text(12:40 mins)
outputTextField = Text(window, width=75, height=6, wrap=WORD, background="white",)
outputTextField.grid(row=4, column=1,sticky=W) # grid position of textField
# run the main loop
window.mainloop()
I tried:
outputTextField.insert.lower(END, userEnteredText)
but that didn't work. Any advice?
If you do outputTextField.insert(END, userEnteredText.lower()) then the entered text will be converted to lower case and then the insert function works as expected, taking the lowercase string as an argument.
in the most cases of these string methods you need to know, that you need to
make a new variable for it.
def onClickSubmitButton(): # submit button event handler
userEnteredText = textbox.get() # get the enterd text from the Entry text box
low = userEnteredText.lower()
outputTextField.delete(0.0, END) # (14:30 mins)delete all of the output field text contents
outputTextField.insert(END, low) # (15:50 mins) output the user eneterd text
So here is a bit fancier option (I don't know if You need something like this but it also does what You ask for except no button has to be pressed to change characters to lowercase, in this case it will do that as the user is typing, try it out):
from tkinter import Tk, Text
def lower_input(event):
if not event.char or r'\x' in repr(event.char):
return
text.delete('insert-1c')
text.insert('insert', event.char.lower())
root = Tk()
text = Text(root)
text.pack()
text.bind('<Key>', lambda e: root.after(1, lower_input, e))
root.mainloop()
Basically it just binds the "<Key>" event to the text widget which means that when any key is pressed (and the focus is on the text widget) the event will be triggered which will then call the given function. Also .bind passes an event argument so that should be handled even if not used however here it is used to detect the character or to stop the function if the pressed key does not have a character (e.g. "Caps Lock" or "Backspace" (which actually has a bytes type thingy so that is why there is also the other comparison of r"\x" in repr(event.char))). Then it just deletes the just written character and places a new one that is in lower case in its place. The root.after is used because the event is called before the character is typed to the Text widget meaning incorrect indexes are used so there is a tiny delay (1 ms) so that the character can appear on the screen and then the indexing happens.
EDIT: it is also possible to add or event.char.islower() (if that doesn't work could also try or not event.char.isupper()) to that if statement to increase the performance a little bit since it won't go through the replacing process if the character is already lowercase
EDIT 2: as per PEP8 You should use snake_case for naming variables and functions and NOT camelCase
Useful sources/docs:
this one specifically about indexes but contains a lot of other very useful stuff too
simple docs I like to use

Remove the default 0 in in integer entry in Tkinter

I am learning to do a GUI using tkinter and I create an integer entry which is working fine except that whenever I run my program the number 0 is already put in the entry, is there anyway to remove it and just have nothing instead? It doesn't do that with strings
I checked answers like this one: https://stackoverflow.com/a/39879154/13061992
but I didn't work (the user already has said that is only for strings but I gave it a shot anyway)
To explain more, I am creating a text box using the following:
tries_var = tk.IntVar()
tries_label = tk.Label(root, text='Number Of Tries', font=('calibre', 10, 'bold'))
tries_entry = tk.Entry(root, textvariable=tries_var, font=('calibre', 10, 'normal'))
tries_label.grid(row=2, column=0)
tries_entry.grid(row=2, column=1)
When I run the program I have 0 written by default, like this:
I want to get rid of this and instead have the box to be empty, any help would be appreciated.
The reason for this is, you are using tk.IntVar() which by default will put a 0 onto the Entry widget. To get rid of this, change tk.IntVar() to tk.StringVar().
tries_var = tk.StringVar()
Though keep in mind, tk.IntVar.get() will return int, now that you are using tk.StringVar, you might need int(tk.StringVar.get()), given that your input is completely numbers.
print(int(tries_var.get()) # Given that, your input is entirely numbers
Try this:
import tkinter as tk
def only_allow_numbers(event):
char = event.char.lower()
if (event.state & 4) >> 2:
# If ctrl is also pressed:
# Check and handle Ctrl+c, Ctrl+v, Ctrl+x
# For now I will just allow it
return None
if char.isprintable() and (not event.char.isdigit()):
return "break"
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
entry.bind("<Key>", only_allow_numbers)
root.mainloop()
It uses bindings. When you return "break" from a binding that key is ignored (isn't inserted in the entry.)

Python/Tkinter autofocus when entry is filled

I am trying to run a simple script with a simple GUI that compares two strings. As you can see I added two entry fields and a check button that runs my check function. At the start the .focus() is on the first entry widget then I need to input a value select the second entry widget input a value and then press check for the script to compare the two given values.
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
win.title("Vitek magacin")
win.geometry("200x150")
win.configure(background='gold')
def check():
c1=no1.get()
c2=no2.get()
if c1 == c2:
print("You win a silly price")
else:
print("You win nothing old men")
no1=tk.StringVar()
no2=tk.StringVar()
inputa = ttk.Entry(win, width=12, textvariable=no1)
inputa.grid(column=0, row=1)
inputa.focus()
inputb = ttk.Entry(win, width=12, textvariable=no2)
inputb.grid(column=0, row=2)
ButtonCheck = ttk.Button(win, text='Check',
command=check)
ButtonCheck.grid(column=0, row=3)
win.mainloop()
So what I am trying to make it do is:
When I fill entry one for it to switch focus to entry two and when entry two is filled I want it to run the check function, delete the entry fields, and start over, because these inputs are going come from a bar code reader and I want it to go as automated as possible. Can anyone please help ?
I would solve this with the bind method.
This is a little wrong and have some disadvantages, but it works if you don't press any additional keys. If you press 'fn' 12 times, for example, there will be a problem, but I think you can solve it.
The idea is that the program counts keystrokes and compares them with the width of the entry field, if the entry field is filled completely, then the program focuses on the second entry field, if the second entry field is also filled completely, then the program performs the function.
from tkinter import *
root = Tk()
root.title("Vitek magacin")
root.geometry("200x150")
root.configure(background='gold')
s = 0
def check():
c1=inputa.get()
c2=inputb.get()
if c1 == c2:
print("You win a silly price")
else:
print("You win nothing old men")
def checker(event):
global s
s += 1
if s==inputa['width']:
inputb.focus()
if s==inputa['width']+inputb['width']:
check()
inputa = Entry(root, width=12)
inputa.grid(column=0, row=1)
inputa.focus()
inputb = Entry(root, width=12)
inputb.grid(column=0, row=2)
ButtonCheck = Button(root, text='Check', command=check)
ButtonCheck.grid(column=0, row=3)
root.bind('<Key>', checker)
root.mainloop()

how do I create entry fields and buttons to get the content from the entry field using iteration in tkinter for python?

I'm trying to make a tkinter GUI with a certain amount of buttons and entry fields that is specified by the user, so in this code below for example if the user specifies the variable number to be 3 I want there to be 3 entry boxes and , and I want to set it up so that if I type a value into the first field and click the button next to it, I want that button to read the value from the entry field next to it. I also need to assign each entry field to a variable that will be created through the same iterative loop. However, I'm having difficulty especially in regards to mapping the buttons to the entry fields, as I always seem to run up against an error with the text "AttributeError: 'NoneType' object has no attribute 'get'". Would anyone be able to either fix my code or help me find an alternative solution to the problem? Sorry if the description's a bit confusing.
This is different from just a problem of just getting the contents of the entry widget as I need it to create a certain amount of entry widgets and buttons using iteration, and the question that my question has been marked a duplicate of doesn't explain how to iteratively map each entry field to each button. For example, if I enter 3 as the variable, I need the program to create entry field 1, entry field 2 and entry field 3 and button 1, button 2 and button 3 and then map each button to its respective entry field using iteration.
I've tried using dictionaries, but this doesn't seem to help much.
import tkinter as tk
root = tk.Tk()
number = 3
d={}
def callBack():
print(d["E{0}".format(i)].get())
return
for i in range(0,number):
d["E{0}".format(i)] = tk.Entry(root)
d["E{0}".format(i)].grid(row=i, column=0)
d["B{0}".format(i)] = tk.Button(root, text="test", command=callBack)
d["B{0}".format(i)].grid(row=i, column=1)
The solution to "'NoneType' object has no attribute 'get'" has been asked probably a hundred times on this site, and the answer is always the same.
In python, when you do x = y().z(), x will be given the value of z(). In the case of x=tk.Entry(...).grid(...), x will be None because grid(...) always returns None. The solution is to always call grid or pack or place separate from when you create a widget.
You also claim you are having problems with dictionaries, but I don't see any problem in your code other than you are making it more difficult than necessary. You can directly use i as an index without having to build up a string for the index. If you need to keep track of both buttons and entries, I recommend two variables rather than one.
Part of the problem may also have to do with the fact you're trying to do something very odd in your command. You're trying to call the get method of the entry, but that's pointless since it simply returns a value that gets thrown away. In almost all cases, the correct solution is to write a proper function rather than trying to squeeze functionality into a lambda.
Example:
def handle_click(i):
entry = entries[i]
print("the value is {}".format(entry.get()))
buttons = {}
entries = {}
for i in range(0,number):
entry = tk.Entry(root)
button = tk.Button(root, text="test", command=lambda i=i: handle_click(i))
buttons[i] = button
entries[i] = entry
entry.grid(row=i, column=0)
button.grid(row=i, column=1)
You need to save the Entry and Button before calling grid:
import tkinter as tk
number = 3
root = tk.Tk()
def get_on_click(widget_dict, entry_name):
def on_click():
result = widget_dict[entry_name].get()
print("%s = %s" % (entry_name, result))
return result
return on_click
d = dict()
for i in range(0, number):
entry_name = "E{0}".format(i)
button_name = "B{0}".format(i)
print(entry_name, button_name)
d[entry_name] = tk.Entry(root)
d[entry_name].grid(row=i, column=0)
d[button_name] = tk.Button(root, text="test", command=get_on_click(d, entry_name))
d[button_name].grid(row=i, column=1)
root.mainloop()
This should help you get started.
In your comment, you ask how to save the value in the Entry. I would create a class to handle everything:
import tkinter as tk
number = 3
root = tk.Tk()
class EntryButton(object):
def __init__(self, root, number):
self.number = number
self.entry = tk.Entry(root)
self.button = tk.Button(root, text="test", command=self.on_click)
self.entry.grid(row=number, column=0)
self.button.grid(row=number, column=1)
self.value = None
def on_click(self):
self.value = self.entry.get()
storage = dict()
for i in range(0, number):
storage[i] = EntryButton(root, i)
root.mainloop()
for i in range(0, number):
value = storage[i].value
print(f"storage[{i}] = {value}")
As you can see, this eliminates a lot of extra work.
For get text from entry
Entry.get("1.0", "end-1c")
# 1.0 for get first line.
# end-1c for if last letter space, this deletes it.
More info

Dynamically generated list of checkboxes in Tkinter only returns 0s

I am writing a small gui with Tkinter in python 2,7. At some point I call a function that creates a popup window that is populated by a number of checkboxes, the number of checkboxes is defined by the attributes variable.
def attribute_select(attributes):
popup = tk.Tk()
popup.wm_title("Attribute selection")
label = ttk.Label(popup, text="Please select which of the following \n attributes will undergo k-anonymity.",
font=NORMAL_FONT)
label.pack(side="top", fill="x", pady=10)
def read_status(key):
var_obj = var.get(key)
print "key is:", key
print "var_obj.get() is:", var_obj.get()
def leave_mini():
popup.destroy()
var = dict()
count = 1
for child in range(attributes):
var[child] = tk.IntVar()
chk = tk.Checkbutton(popup, text='Attribute: '+str(count), variable=var[child], justify="left", onvalue=1,
offvalue=0, command=lambda key=child: read_status(key))
count += 1
chk.pack()
print var
exit_button = ttk.Button(popup, text="OK", command=leave_mini)
exit_button.pack()
popup.mainloop()
Everything runs just fine but when I try to check one of the boxes the variable value doesn't change the printout every time is: [ key is: 0 var_obj.get() is: 0 ] or [ key is: 5 var_obj.get() is: 0 ]. So the key is proper for every box but the variable doesn't change. I'm sure it's a simple fix I just can't see it... any ideas?
You must not create more than one instance of Tk. By creating an instance in attribute_select I must assume you've create the "real" root window somewhere else. One of the side effects of creating more than one instance of Tk is that your tkinter variables (IntVar, etc) don't behave the way you expect them to.
If you need popup windows, create instances of Toplevel.

Categories