Getting variable out of Tkinter - python

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)

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()

Tkinter: all radio buttons are selected

Why in this code when clicking a button when a new window opens, all the radio buttons are selected?
class CodeButton:
def __init__(self, root):
self.btn = Button(root, text="Code",width=20, height=1,bg="white", fg="black")
self.btn.bind("<Button-1>", make_code_window)
self.btn.pack()
def make_code_window(event):
new_root = Toplevel()
new_root.minsize(width=300, height=300)
var = IntVar()
var.set(0)
for i in range(8):
Radiobutton(new_root, text=str(i), variable=var, value=i).pack()
def main():
root = Tk()
root.minsize(width=400, height=250)
CodeButton(root)
root.mainloop()
It's got something to do with storing the IntVar in a local variable in the function that will be discarded as soon as the make_code_window() function returns. You can fix the problem by making the IntVar an attribute of the new_root window widget, so it will exist at least as long as the widget using it does.
The code in your example isn't very realistic in the sense that typically one would want to use the current value of the IntVar for something somewhere else in the Python code, but that wouldn't be possible since it's only stored temporarily in local variable which exists only during the execution of the function that created it.
try:
from tkinter import *
except ImportError: # Python 2
from Tkinter import *
class CodeButton:
def __init__(self, root):
self.btn = Button(root, text="Code",width=20, height=1,bg="white", fg="black")
self.btn.bind("<Button-1>", make_code_window)
self.btn.pack()
def make_code_window(event):
new_root = Toplevel()
new_root.minsize(width=300, height=300)
var = new_root.var = IntVar() # changed
var.set(0)
for i in range(8):
Radiobutton(new_root, text=str(i), variable=var, value=i).pack()
def main():
root = Tk()
root.minsize(width=400, height=250)
CodeButton(root)
root.mainloop()
main()
(Following-up on the discussion we were having in the comments section of my other answer.)
Yes, passing the IntVar as an argument to the event handler function is a little tricky—in fact it's sometimes called The extra arguments trick. ;-)
Here's an example of applying it to your code:
try:
from tkinter import *
except ImportError: # Python 2
from Tkinter import *
class CodeButton:
def __init__(self, root):
self.btn = Button(root, text="Code",width=20, height=1,bg="white", fg="black")
self.btn.bind("<Button-1>",
# Extra Arguments Trick
lambda event, var=root.var: make_code_window(event, var))
self.btn.pack()
def make_code_window(event, var): # note added "var" argument
new_root = Toplevel()
new_root.minsize(width=300, height=300)
var.set(-99) # deselect by using value not associated with any RadioButtons
for i in range(8):
Radiobutton(new_root, text=str(i), variable=var, value=i).pack()
def main():
root = Tk()
root.minsize(width=400, height=250)
root.var = IntVar() # create it here to give access to it in the rest of your code
CodeButton(root)
root.mainloop()
main()

How do you close a tkinter window in another function?

I want a button in my window to open a new window and close the previous one. Is it possible to have one button do both of these? I've tried in the following code, but it hasn't worked, just told me that window is not defined:
import tkinter
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = window2).pack()
window.mainloop()
def window2():
window.destroy() #This is where the error is
menu = tkinter.Tk()
etc, etc, etc
window1()
First, you need to return the window object from the first function:
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = lambda: window2(window)).pack()
window.mainloop()
return window
Then, you need to pass the window as an argument to your function:
def window2(window):
window.destroy()
menu = tkinter.Tk()
And then call window1 with:
window = window1()
and click the button to destroy it and do the rest
This is an example using Toplevels, which is usually a better choice than creating, destroying, re-creating Tk() instances. The unique Toplevel ID is passed to the close_it function using partial(). You would, of course, combine them or have the close function call the open function.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
from functools import partial
class OpenToplevels():
""" open and close additional Toplevels with a button
"""
def __init__(self):
self.root = tk.Tk()
self.button_ctr=0
but=tk.Button(self.root, text="Open a Toplevel",
command=self.open_another)
but.grid(row=0, column=0)
tk.Button(self.root, text="Exit Tkinter", bg="red",
command=self.root.quit).grid(row=1, column=0, sticky="we")
self.root.mainloop()
def close_it(self, id):
id.destroy()
def open_another(self):
self.button_ctr += 1
id = tk.Toplevel(self.root)
id.title("Toplevel #%d" % (self.button_ctr))
tk.Button(id, text="Close Toplevel #%d" % (self.button_ctr),
command=partial(self.close_it, id),
bg="orange", width=20).grid(row=1, column=0)
Ot=OpenToplevels()
Yes. Is possible. But you'll need to def that:
def window1:
blablabla
blablabla
def window2:
window2.destroy() <-- Here where the error was
How you noticed, put your name of window what you want Destroy and it will work!
using Python3
You could use a "global" such as:
root = Tk()
root.title('This is the root window')
def window_create():
global window_one
window_one = Tk()
window_one.title('This is window 1')
Then, from any function (or elsewhere) when you want to destroy window_one, do:
def window_destroyer():
window_one.destroy()
You could call your window_destroyer function from a button anywhere such as root which the example shows:
kill_window_btn = Button(root, text="Destroy", command=window_destroyer).pack()
Of course, follow your own naming conventions. :)
It seems to me, just 'global window_one' would solve it.

Tkinter Entry widget requires two events to update

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()

Categories