Tkinter Entry widget requires two events to update - python

I've been trying to develop a small gui to calculate some totals with weighting etc. While trying to get a total to update real-time with a changing entry, I noticed it took two events to update; This is a simplified version of my code that shows the problem:
from Tkinter import *
root=Tk()
frame=Frame(root)
frame.pack()
entry=Entry(frame)
entry.pack()
label=Label(frame,text="entry:")
label.pack()
def updatelabel(event):
label=Label(frame,text="entry:"+entry.get())
label.pack()
print "called"
entry.bind("<Key>", updatelabel)
root.mainloop()
When you input into the field the function calls, but does not update to what was typed until the next character is typed. How would I go about getting the label to update to what is in the field at the time?

You don't really need to explicitly process events and use callback functions to accomplish what you want. In other words it's possible to get Tkinter to do it for you automatically using a StringVar().
from Tkinter import *
root=Tk()
frame=Frame(root)
frame.pack()
entry_var = StringVar()
entry_var.set('')
entry = Entry(frame, textvariable=entry_var)
entry.pack()
label = Label(frame, text='entry: ')
label.pack(side=LEFT)
contents = Label(frame, textvariable=entry_var)
contents.pack(side=LEFT)
entry.focus_set() # force initial keyboard focus to be on entry widget
root.mainloop()

Instead of using entry.bind("<Key>", update label), I used the root window instead: root.bind("<Key>", update label). This did the trick, however it is important to realize that the function updatelabel() will be called every time a key is pressed in your tkinter window. This could cause some problems if you have more than one entry box updating labels.
Here is the code I wrote with a few modifications:
from Tkinter import *
root=Tk()
frame=Frame(root)
frame.pack()
update_label = StringVar() # Made a StringVar so you don't get new labels every time a key is pressed.
update_label.set("entry:")
entry=Entry(frame)
entry.pack()
label=Label(frame,textvariable=update_label) # Used textvariable= instead of text=
label.pack()
def updatelabel(event):
update_label.set("entry:" + entry.get()) # Setting the variable used in textvariable=
print "called"
root.bind("<Key>", updatelabel) # Changed entry.bind to root.bind
root.mainloop()

No it doesn't require two entries to be called, it is called on the first entry. The key bindings are on the Entry widgets to avoid the problems that a binding to the root will create if you have more than one entry widget.
import tkinter as tk
class SmallApp(tk.Frame):
def __init__(self, master = None):
tk.Frame.__init__(self, master)
self.master = master
self.pack()
self.entry = tk.Entry(self)
self.entry.pack()
self.var = "entry:"
self.label = tk.Label(text = self.var)
self.label.pack()
self.entry.bind("<Key>", self.updatelabel)
def updatelabel(self, event):
self.var += event.char
self.label.configure(text=self.var)
root = tk.Tk()
app = SmallApp(root)
app.mainloop()

Related

Is there a way to get values of dynamically added text fields in tkinter?

For homework, I have to create an application that creates a text field everytime a user clicks a button, and then get values from the fields when "submit" button is pressed.
The trace method shows up repeatedly, but I do not know how to use it. I know it requires a callback function, but what should that callback function be?
from tkinter import *
from tkinter import ttk
import sqlite3
import getpass
import wipComingIn
class Application(object):
def __init__(self,master):
self.master=master
self.ScanWIPIn = Button(master, text="Scan WIP In", width=25,
font='Calibri 12
bold',background='snow',command=self.scanWIPIn).grid(row=0, column=0,
padx=10)
def scanWIPIn(self):
incomingInventory=wipComingIn.scanIn()
def main():
root = Tk()
app=Application(root)
root.title("Main Menu")
root.configure(background="light cyan")
root.resizable(0, 0)
root.geometry('230x230+300+80')
root.mainloop()
if __name__=='__main__':
main()
class scanIn(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.geometry('300x100+350+100')
self.title('Scan In')
self.resizable(0,0)
self.num_rows=1
self.LocationLb = Label(self,text='Scan Location:',font='Arial
12').grid(row=1,column=1)
self.LocationBCText = Entry(self).grid(row=1,column=2)
self.AddLotBtn= Button(self,text="Scan
Lot",command=self.addField).grid(row=2,column=1)
self.CompleteTransaction =
Button(self,text="Complete",command=self.AddEntry).grid(row=2,column=4)
global listOfLots
listOfLots=[]
listOfLocation=[]
global rowNum
rowNum=2
def addField(self):
height =Toplevel.winfo_height(self)
height=height+25
global rowNum
rowNum=rowNum+1
listOfLots.append(StringVar())
newLot = Entry(self, textvariable=listOfLots[rowNum - 2])
newLot.grid(row=rowNum,column=2, pady=1)
listOfLots.append(StringVar())
geometryText='300'+str(height)+'350+100'
print(geometryText)
self.geometry('300x'+str(height)+'+350+100')
newLot.focus_set()
You could try just making a class that does it, for example:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('200x200')
class EntryListWidget(ttk.Frame):
"""Widget that creates a column of entry boxes."""
def __init__(self, master):
super().__init__(master)
self.entries = []
def add_entry(self):
"""Creates a new entry box and keeps reference to respective variable."""
entry_var = tk.StringVar()
self.entries.append(entry_var)
ttk.Entry(self, textvariable=entry_var).pack()
def get_entries(self):
"""Gets each entrybox text and returns as list."""
return [entry.get() for entry in self.entries]
entry_widget = EntryListWidget(root)
entry_widget.pack()
# Buttons to control adding new entry and getting their values
ttk.Button(root, text='Add Entry', command=entry_widget.add_entry).pack()
ttk.Button(root, text='Get Entries', command=entry_widget.get_entries).pack()
root.mainloop()
Just using the variable classes and not trace; I actually wouldn't use trace in this situation because I believe trace uses the callback every time the variable changes and here you have a one time "submit" button that collects all the values. You could extend this class idea to get what you're looking to do I bet.

how can i use the Entry function in tkinter in an if argument?

So i am making a password organisator in python, and i don't know how i can get user input from an Entry and use it in an if argument?
text1 = StringVar()
def but():
text1.get()
print(text1.get())
knapp2 = Button(root, command="but").pack()
entry1 = Entry(root, textvariable=text1).place(x=270, y=100)
You can call the .get() function on on the Entry widget too to get the text.
import tkinter
from tkinter import Tk, Button, Entry
mw = Tk()
entry = Entry(mw)
entry.pack()
def but():
text = entry.get()
print(text)
button.config(text='Button Clicked')
button = Button(mw, command=but, text='Test')
button.pack()
mw.mainloop()
This code does work but will become complicated with larger code. You will have to define the function before creating a widget that calls that function. In the above example if you created the button widget before the function you would get an exception. You could create the widget, then create the function, then change the configuration of the button to call that function when clicked but that's still pretty complicated and will be confusing in large programs.
I would recommend putting everything in a class. It makes it easy to reference widgets in functions.
import tkinter
from tkinter import Tk, Button, Entry
class Main:
def __init__(self, master):
self.master = master
self.entry = Entry(self.master)
self.entry.pack()
self.button = Button(self.master, text='Test', command=self.But)
self.button.pack()
def But(self):
print(self.entry.get())
self.button.config(text='Button Clicked.')
mw = Tk()
main = Main(mw)
mw.mainloop()

Getting variable out of Tkinter

I would like to ask if anyone knows how to get out a variable from an Entry in Tkinter to be used in future calculation.
Let us assume that I want to create a prompt where the user needs to place two numbers in the two different Entry widgets.
These numbers are to be used in another script for calculation. How can I retrieve the values from the prompt created in Tkinter?
In my opinion, I would need to create a function with the code bellow and make it return the value from the Tkinter prompt. However, I cannot return the numbers because I'm destroying the root window. How can I get pass this, preferably without global variables.
Best Regards
from tkinter import *
from tkinter import ttk
#Start of window
root=Tk()
#title of the window
root.title('Title of the window')
def get_values():
values=[(),(value2.get())]
return values
# Creates a main frame on the window with the master being the root window
mainframe=ttk.Frame(root, width=500, height=300,borderwidth=5, relief="sunken")
mainframe.grid(sticky=(N, S, E, W))
###############################################################################
#
#
# Label of the first value
label1=ttk.Label(master=mainframe, text='First Value')
label1.grid(column=0,row=0)
# Label of the second value
label2=ttk.Label(master=mainframe, text='Second Value')
label2.grid(column=0,row=1)
###############################################################################
#
#
# Entry of the first value
strvar1 = StringVar()
value1 = ttk.Entry(mainframe, textvariable=strvar1)
value1.grid(column=1,row=0)
# Entry of the second value
strvar2 = StringVar()
value2 = ttk.Entry(mainframe, textvariable=strvar2)
value2.grid(column=1,row=1)
# Creates a simplle button widget on the mainframe
button1 = ttk.Button(mainframe, text='Collect', command=get_values)
button1.grid(column=2,row=1)
# Creates a simplle button widget on the mainframe
button2 = ttk.Button(mainframe, text='Exit', command=root.destroy)
button2.grid(column=2,row=2)
root.mainloop()
You use a class because the class instance and it's variables remain after tkinter exits.https://www.tutorialspoint.com/python/python_classes_objects.htm And you may want to reexamine some of your documentation requirements, i.e. when the statement is
"root.title('Title of the window')", adding the explanation "#title of the window" is just a waste of your time..
""" A simplified example
"""
import sys
if 3 == sys.version_info[0]: ## 3.X is default if dual system
import tkinter as tk ## Python 3.x
else:
import Tkinter as tk ## Python 2.x
class GetEntry():
def __init__(self, master):
self.master=master
self.entry_contents=None
self.e = tk.Entry(master)
self.e.grid(row=0, column=0)
self.e.focus_set()
tk.Button(master, text="get", width=10, bg="yellow",
command=self.callback).grid(row=10, column=0)
def callback(self):
""" get the contents of the Entry and exit
"""
self.entry_contents=self.e.get()
self.master.quit()
master = tk.Tk()
GE=GetEntry(master)
master.mainloop()
print("\n***** after tkinter exits, entered =", GE.entry_contents)
So, I have taken Curly Joe's example and made a function with the his sketch
The final result, for anyone wanting to use this as a template for a input dialog box:
def input_dlg():
import tkinter as tk
from tkinter import ttk
class GetEntry():
def __init__(self, master):
self.master=master
self.master.title('Input Dialog Box')
self.entry_contents=None
## Set point entries
# First point
self.point1 = ttk.Entry(master)
self.point1.grid(row=0, column=1)
self.point1.focus_set()
# Second point
self.point2 = ttk.Entry(master)
self.point2.grid(row=1, column=1)
self.point2.focus_set()
# labels
ttk.Label(text='First Point').grid(row=0, column=0)
ttk.Label(text='Second Point').grid(row=1, column=0)
ttk.Button(master, text="Done", width=10,command=self.callback).grid(row=5, column=2)
def callback(self):
""" get the contents of the Entries and exit the prompt"""
self.entry_contents=[self.point1.get(),self.point2.get()]
self.master.destroy()
master = tk.Tk()
GetPoints=GetEntry(master)
master.mainloop()
Points=GetPoints.entry_contents
return list(Points)
In python, functions are objects, as in get_values is an object.
Objects can have attributes.
Using these two, and the knowledge that we can't really return from a button command, we can instead attach an attribute to an already global object and simply use that as the return value.
Example with button
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
def on_button_press(entry):
on_button_press.value = entry.get()
entry.quit()
def main():
root = tk.Tk()
entry = tk.Entry(root)
tk.Button(root, text="Get Value!", command=lambda e = entry : on_button_press(e)).pack()
entry.pack()
tk.mainloop()
return on_button_press.value
if __name__ == '__main__':
val = main()
print(val)
Minimalistic example
Similarly modules are also objects, if you want to avoid occupying global namespace extremely, you can attach a new attribute to the module you're using
See:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
if __name__ == '__main__':
tk.my_value = lambda: [setattr(tk, 'my_value', entry.get()), root.destroy()]
root = tk.Tk()
entry = tk.Entry(root)
root.protocol('WM_DELETE_WINDOW', tk.my_value)
entry.pack()
tk.mainloop()
print(tk.my_value)

Entry data manipulation difficulties [duplicate]

I'm trying to use an Entry field to get manual input, and then work with that data.
All sources I've found claim I should use the get() function, but I haven't found a simple working mini example yet, and I can't get it to work.
I hope someone can tel me what I'm doing wrong. Here's a mini file:
from tkinter import *
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
content = entry.get()
print(content) # does not work
mainloop()
This gives me an Entry field I can type in, but I can't do anything with the data once it's typed in.
I suspect my code doesn't work because initially, entry is empty. But then how do I access input data once it has been typed in?
It looks like you may be confused as to when commands are run. In your example, you are calling the get method before the GUI has a chance to be displayed on the screen (which happens after you call mainloop.
Try adding a button that calls the get method. This is much easier if you write your application as a class. For example:
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.pack()
self.entry.pack()
def on_button(self):
print(self.entry.get())
app = SampleApp()
app.mainloop()
Run the program, type into the entry widget, then click on the button.
You could also use a StringVar variable, even if it's not strictly necessary:
v = StringVar()
e = Entry(master, textvariable=v)
e.pack()
v.set("a default value")
s = v.get()
For more information, see this page on effbot.org.
A simple example without classes:
from tkinter import *
master = Tk()
# Create this method before you create the entry
def return_entry(en):
"""Gets and prints the content of the entry"""
content = entry.get()
print(content)
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
# Connect the entry with the return button
entry.bind('<Return>', return_entry)
mainloop()
*
master = Tk()
entryb1 = StringVar
Label(master, text="Input: ").grid(row=0, sticky=W)
Entry(master, textvariable=entryb1).grid(row=1, column=1)
b1 = Button(master, text="continue", command=print_content)
b1.grid(row=2, column=1)
def print_content():
global entryb1
content = entryb1.get()
print(content)
master.mainloop()
What you did wrong was not put it inside a Define function then you hadn't used the .get function with the textvariable you had set.
you need to put a textvariable in it, so you can use set() and get() method :
var=StringVar()
x= Entry (root,textvariable=var)
Most of the answers I found only showed how to do it with tkinter as tk. This was a problem for me as my program was 300 lines long with tons of other labels and buttons, and I would have had to change a lot of it.
Here's a way to do it without importing tkinter as tk or using StringVars. I modified the original mini program by:
making it a class
adding a button and an extra method.
This program opens up a tkinter window with an entry box and an "Enter" button. Clicking the Enter button prints whatever is in the entry box.
from tkinter import *
class mini():
def __init__(self):
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
Button(master, text='Enter', command=self.get_content).grid(row=1)
self.entry = Entry(master)
self.entry.grid(row=0, column=1)
master.mainloop()
def get_content(self):
content = self.entry.get()
print(content)
m = mini()

.get() not giving a value [duplicate]

I'm trying to use an Entry field to get manual input, and then work with that data.
All sources I've found claim I should use the get() function, but I haven't found a simple working mini example yet, and I can't get it to work.
I hope someone can tel me what I'm doing wrong. Here's a mini file:
from tkinter import *
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
content = entry.get()
print(content) # does not work
mainloop()
This gives me an Entry field I can type in, but I can't do anything with the data once it's typed in.
I suspect my code doesn't work because initially, entry is empty. But then how do I access input data once it has been typed in?
It looks like you may be confused as to when commands are run. In your example, you are calling the get method before the GUI has a chance to be displayed on the screen (which happens after you call mainloop.
Try adding a button that calls the get method. This is much easier if you write your application as a class. For example:
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.pack()
self.entry.pack()
def on_button(self):
print(self.entry.get())
app = SampleApp()
app.mainloop()
Run the program, type into the entry widget, then click on the button.
You could also use a StringVar variable, even if it's not strictly necessary:
v = StringVar()
e = Entry(master, textvariable=v)
e.pack()
v.set("a default value")
s = v.get()
For more information, see this page on effbot.org.
A simple example without classes:
from tkinter import *
master = Tk()
# Create this method before you create the entry
def return_entry(en):
"""Gets and prints the content of the entry"""
content = entry.get()
print(content)
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
# Connect the entry with the return button
entry.bind('<Return>', return_entry)
mainloop()
*
master = Tk()
entryb1 = StringVar
Label(master, text="Input: ").grid(row=0, sticky=W)
Entry(master, textvariable=entryb1).grid(row=1, column=1)
b1 = Button(master, text="continue", command=print_content)
b1.grid(row=2, column=1)
def print_content():
global entryb1
content = entryb1.get()
print(content)
master.mainloop()
What you did wrong was not put it inside a Define function then you hadn't used the .get function with the textvariable you had set.
you need to put a textvariable in it, so you can use set() and get() method :
var=StringVar()
x= Entry (root,textvariable=var)
Most of the answers I found only showed how to do it with tkinter as tk. This was a problem for me as my program was 300 lines long with tons of other labels and buttons, and I would have had to change a lot of it.
Here's a way to do it without importing tkinter as tk or using StringVars. I modified the original mini program by:
making it a class
adding a button and an extra method.
This program opens up a tkinter window with an entry box and an "Enter" button. Clicking the Enter button prints whatever is in the entry box.
from tkinter import *
class mini():
def __init__(self):
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
Button(master, text='Enter', command=self.get_content).grid(row=1)
self.entry = Entry(master)
self.entry.grid(row=0, column=1)
master.mainloop()
def get_content(self):
content = self.entry.get()
print(content)
m = mini()

Categories