tkinter code after mainloop() not being executed after closing window - python

I need help using tkinter in python.
I want to use a while loop inside my class so that it keeps printing in the terminal the content of an entry (box1). But the problem is that if I put a loop in my class the entry wont even be created because the root.mainloop() is after my while loop.
The program:
from tkinter import *
from tkinter import ttk
import tkinter as tk
class root(Tk):
def __init__(self):
super(root, self).__init__()
self.minsize(500,400)
self.configure(bg='#121213')
self.createEntry()
def createEntry(self):
self.name1 = StringVar()
self.box1 = ttk.Entry(self, width=2, textvariable = self.name1, font="Calibri 15")
self.box1.place(x=128, y=31)
while True:
print(self.name1.get())
root=root()
root.mainloop()
If I put the loop after the root.mainloop() it won't start printing the contents of name1 as long as the tkinter file is open. So it will print only the final version of name1 in a loop:
The Code:
from tkinter import *
from tkinter import ttk
import tkinter as tk
class root(Tk):
def __init__(self):
super(root, self).__init__()
self.minsize(500,400)
self.configure(bg='#121213')
self.createEntry()
def createEntry(self):
self.name1 = StringVar()
self.box1 = ttk.Entry(self, width=2, textvariable = self.name1, font="Calibri 15")
self.box1.place(x=128, y=31)
while True:
print(self.name1.get())
root=root()
root.mainloop()
while True:
print(root.name1.get())
Video of my problem
Does anyone have any solution?

How about a solution that does not put constant drain on the processing power?
If you add a callback to StringVar, you can trigger a function every time the variable is changed, and only then. Infinite loops don't work very well with UI applications, as in order to avoid stopping the control flow of the app you have to use things like async, threading etc.
def createEntry(self):
self.name1 = StringVar()
self.name1.trace_add("write", lambda name, index, mode: print(self.name1.get()))
self.box1 = ttk.Entry(self, width=2, textvariable = self.name1, font="Calibri 15")
self.box1.place(x=128, y=31)
This piece of code will make given lambda function trigger every time something is written to name1 variable.

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)

getting information from Entry

my name is Rod. I recently started programming with OOP and it's not yet quite clear to me. I want to make my Button get information from my four entries but i don't know how to say to the program to get it from the four of them at the same time. I know i have to use the get() method but i don't understand how to insert it in the class so it will recognize my four Entries. Thanks!
from tkinter import *
from tkinter import ttk
class Application(Frame):
def __init__(self):
Frame.__init__(self)
self.grid()
def createButton(self,b_text,b_command,r,c):
self.newButton = Button(self, text=b_text,command=b_command)
self.newButton.grid(padx=20, pady=10, row=r,column=c)
def createEntry(self,px,r,c):
text = StringVar()
self.newEntry = Entry(self,width=8,textvariable=text)
self.newEntry.grid(padx=px, pady=10,row=r,column=c)
def printEntryData():
#code here
app = Application()
entry1 = app.createEntry(20,0,0)
entry2 = app.createEntry(20,0,1)
entry3 = app.createEntry(20,0,2)
entry4 = app.createEntry(20,0,3)
app.createButton("add",printEntryData,1,6)
app.mainloop()
Every time you make an entry you overwrite the previous value of text. All those previous Entry boxes now are orphans: there's no way to access them to get the information out. (they would have been inaccessible anyway since they are local variables).
Instead, you could add the new StringVars to a container like a list, so that you have access to all of them.
from tkinter import *
from tkinter import ttk
class Application(Frame):
def __init__(self):
Frame.__init__(self)
self.entry_list = []
self.grid()
def createButton(self,b_text,b_command,r,c):
self.newButton = Button(self, text=b_text,command=b_command)
self.newButton.grid(padx=20, pady=10, row=r,column=c)
def createEntry(self,px,r,c):
text = StringVar()
self.newEntry = Entry(self,width=8,textvariable=text)
self.newEntry.grid(padx=px, pady=10,row=r,column=c)
self.entry_list.append(text)
def printEntryData():
for entry in app.entry_list:
print(entry.get())
app = Application()
app.createEntry(20,0,0)
app.createEntry(20,0,1)
app.createEntry(20,0,2)
app.createEntry(20,0,3)
app.createButton("add",printEntryData,1,6)
app.mainloop()

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.

Categories