I’m trying to test the existence of a toplevel using the winfo_exists() method made available by Tkinter.
However, I cannot test the existence of a Toplevel by passing the name of this to winfo_exists() method.
I enclose a script to better clarify what I would like to do.
When you open the app, four buttons are shown. Pressing on the first appears a mask where we can, by pressing the relevant buttons, see the execution of both the winfo_exists() method and the winfo_children() method.
Selecting a Toplevel to test using the radiobox on the right happens that if the relative Toplevel is open the function work fine, otherwise generates the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "/home/bc/Desktop/simple_app.py", line 113, in on_test_exist
if self.nametowidget(DICT_TOPLEVELS[self.option.get()]).winfo_exists():
File "/usr/lib/python3.7/tkinter/__init__.py", line 1353, in nametowidget
w = w.children[n]
KeyError: 'toptwo'
Do you have any suggestions?
Basically in an application with many Toplevels, how do you see if a certain Toplevel is open?
Thank you for your attention.
#!/usr/bin/python3
import sys
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
DICT_TOPLEVELS = {0:".topone",
1:".toptwo",
2:".topthree"}
class Main(ttk.Frame):
def __init__(self, parent, ):
super().__init__(name="main")
self.parent = parent
self.init_ui()
def init_ui(self):
f0 = tk.Frame(self)
bts = [("Top One", 4, self.on_open_one, "<Alt-o>"),
("Top Two", 4, self.on_open_two, "<Alt-t>"),
("Top Three", 5, self.on_open_three, "<Alt-w>"),
("Close", 0, self.parent.on_exit, "<Alt-c>")]
for btn in bts:
ttk.Button(f0,
text=btn[0],
underline=btn[1],
command = btn[2]).pack(fill=tk.X, padx=5, pady=5)
self.parent.bind(btn[3], btn[2])
f0.pack()
def on_open_one(self, evt=None):
obj = TopOne(self)
obj.on_open()
def on_open_two(self, evt=None):
obj = TopTwo(self)
obj.on_open()
def on_open_three(self, evt=None):
obj = TopThree(self)
obj.on_open()
class TopOne(tk.Toplevel):
def __init__(self, parent,):
super().__init__(name="topone")
self.parent = parent
self.minsize(200, 100)
self.voices = ('topone','toptwo','topthree')
self.option = tk.IntVar()
self.init_ui()
def init_ui(self):
f0 = tk.Frame(self, bg='SlateGray4', padx=5,pady=5)
f1 = tk.Frame(f0,)
bts = [("Call winfo_exists", 7, self.on_winfo_exists, "<Alt-c>"),
("Get winfo_children", 0, self.on_get_winfo_children, "<Alt-g>"),
("Test exist", 0, self.on_test_exist, "<Alt-t>"),
("Close", 0, self.master.on_exit, "<Alt-c>")]
for btn in bts:
ttk.Button(f1,
text=btn[0],
underline=btn[1],
command = btn[2]).pack(fill=tk.X, padx=5, pady=5)
self.parent.bind(btn[3], btn[2])
f2 = tk.Frame(f0,)
ttk.Label(f2, text="Toplevels:").pack()
for index, text in enumerate(self.voices):
ttk.Radiobutton(f2,
text=text,
variable=self.option,
value=index,).pack()
f0.pack(fill=tk.BOTH, expand=1, padx=5, pady=5)
f2.pack(side=tk.RIGHT, fill=tk.Y, padx=5, pady=5, expand=0)
f1.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5, expand=0)
def on_open(self):
msg = "{0}".format(self.winfo_name().title())
self.title(msg)
def on_winfo_exists(self, evt=None):
for obj in self.nametowidget(".").winfo_children():
if self.nametowidget(obj).winfo_exists():
print("{0} is exist!".format(self.nametowidget(obj).winfo_name()))
def on_get_winfo_children(self, evt=None):
for obj in self.nametowidget(".").winfo_children():
print(self.master.nametowidget(obj))
def on_test_exist(self):
print("You have selected to test if {0} Toplevel exist.".format(DICT_TOPLEVELS[self.option.get()]),)
if self.nametowidget(DICT_TOPLEVELS[self.option.get()]).winfo_exists():
print("Well Toplevel named {0} exists!".format(self.nametowidget(DICT_TOPLEVELS[self.option.get()]).winfo_name()))
else:
print("I’m sorry but Toplevel named {0} doesn’t exist!".format(DICT_TOPLEVELS[self.option.get()]))
class TopTwo(tk.Toplevel):
def __init__(self, parent,):
super().__init__(name="toptwo")
self.parent = parent
self.minsize(200, 100)
self.init_ui()
def init_ui(self):
f = tk.Frame(self, bg='DarkSeaGreen4', padx=5,pady=5)
f.pack(fill=tk.BOTH, expand=1, padx=5, pady=5)
def on_open(self):
msg = "{0}".format(self.winfo_name().title())
self.title(msg)
class TopThree(tk.Toplevel):
def __init__(self, parent,):
super().__init__(name="topthree")
self.parent = parent
self.minsize(200, 100)
self.init_ui()
def init_ui(self):
f = tk.Frame(self, bg='DarkSeaGreen3', padx=5,pady=5)
f.pack(fill=tk.BOTH, expand=1, padx=5, pady=5)
def on_open(self):
msg = "{0}".format(self.winfo_name().title())
self.title(msg)
class App(tk.Tk):
"""Main Application start here"""
def __init__(self,):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
#self.resizable(width=False, height=False)
self.geometry('400x200')
self.title("Simple App")
Main(self).pack(fill=tk.BOTH, expand=1)
def on_exit(self):
msg = "Do you want to quit?"
if messagebox.askokcancel(self.title(), msg, parent=self):
self.destroy()
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
Related
I am trying to get Tkinter to return the value from the input box, but I cannot seem to get it to work.
This is my first attempt trying Tkinter out, s
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\rik\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "n:\regnskab\Afregning\RIK\Udvikling\gui\app2.py", line 43, in step_1
periode_2 = get_period(self)
NameError: name 'get_period' is not defined
import tkinter as tk
from tkinter import ttk
class Mainframe(tk.Tk):
def __init__(self, *args, **kvargs):
super().__init__(*args, **kvargs)
self.title("app")
UserInputFrame(self).grid(row=1, column=0)
ControlsFrame(self).grid(row=2, column=0)
ButtonFrame(self).grid(row=3, column=0)
class UserInputFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.user_input_year = tk.StringVar(value="2021")
label_entry_year = ttk.Label(self, text="Year")
self.entry_year = ttk.Entry(self, textvariable=self.user_input_year)
label_entry_year.grid(row=2, column=0, sticky="W", padx=10, pady=5)
self.entry_year.grid(row=2, column=1, sticky="EW")
def get_period(self):
print(self.entry_year.get())
class ControlsFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
step_1_button = ttk.Button(self, text="Step 1", command=self.step_1)
step_1_button.grid(row=10, column=0, sticky="EW")
def step_1(self):
periode_2 = get_period(self)
print("step 1")
class ButtonFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
quit_button = ttk.Button(self, text="Quit", command=quit)
quit_button.grid(row=4, column=1, sticky="EW")
def quit(self):
app.destroy
app = Mainframe()
app.mainloop()
You have to learn OOP :)
get_period() is not standalone function but it is function inside class UserInputFrame and you try to access it in class ControlsFrame. You can't access function for one class in other class this way.
You would have to get instance of UserInputFrame,
user_input_frame = UserInputFrame(self)
user_input_frame.grid(row=1, column=0)
send it as argument to ControlsFrame
ControlsFrame(self, user_input_frame).grid(row=2, column=0)
keep it in class variable
class ControlsFrame(ttk.Frame):
def __init__(self, container, user_input_frame):
super().__init__(container)
self.user_input_frame = user_input_frame
and then you can use it
def step_1(self):
self.user_input_frame.get_period() # without `self` in `get_period()`
OR better method
You should assing UserInputFrame to class variable in Mainframe
self.user_input_frame = UserInputFrame(self)
self.user_input_frame.grid(row=1, column=0)
and then other objects (created in Mainframe) may access Mainframe (which is its paret/master) using self.master and then they can access elements in Mainframe like self.master.userinputframe
periode_2 = self.master.user_input_frame.get_period() # without `self` in `get_period()`
import tkinter as tk
from tkinter import ttk
class Mainframe(tk.Tk):
def __init__(self, *args, **kvargs):
super().__init__(*args, **kvargs)
self.title("app")
self.user_input_frame = UserInputFrame(self)
self.user_input_frame.grid(row=1, column=0)
ControlsFrame(self).grid(row=2, column=0)
ButtonFrame(self).grid(row=3, column=0)
class UserInputFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.user_input_year = tk.StringVar(value="2021")
label_entry_year = ttk.Label(self, text="Year")
self.entry_year = ttk.Entry(self, textvariable=self.user_input_year)
label_entry_year.grid(row=2, column=0, sticky="W", padx=10, pady=5)
self.entry_year.grid(row=2, column=1, sticky="EW")
def get_period(self):
#print(self.entry_year.get())
return self.entry_year.get()
class ControlsFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
step_1_button = ttk.Button(self, text="Step 1", command=self.step_1)
step_1_button.grid(row=10, column=0, sticky="EW")
def step_1(self):
# `self.master` gives access to parent object which here is `Masterframe` (because you used `Masterframe` as `self` when you created `ControlsFrame(self)`
periode_2 = self.master.user_input_frame.get_period() # without `self` in `get_period()`
print("step 1")
class ButtonFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
quit_button = ttk.Button(self, text="Quit", command=self.quit)
quit_button.grid(row=4, column=1, sticky="EW")
def quit(self):
app.destroy()
app = Mainframe()
app.mainloop()
BTW: in ButtonFrame you forgot self. in command=self.quit and () in app.destroy()
EDIT: as #acw1668 noticed function get_period() doesn't return value from entry - it only print it - so it needs return
def get_period(self):
return self.entry_year.get()
When attempting to create a second Toplevel in Tkinter after closing the first I get the error:
_tkinter.TclError: bad window path name ".!toplevel
The error only occurs when the first Toplevel is closed, when I run the code without close_window() no error occurs and new_window works and creates the second Toplevel. I need to be able to close the first Toplevel and am not sure what is going wrong here so any help is much appreciated.
Here is a minimal reproducible example.
import tkinter as tk
class auto_haven:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.frame.place(relwidth=1, relheight=1)
self.admin_login_button = tk.Button(self.frame, text="Admin Login", font=40, command=self.new_window)
self.admin_login_button.place(relwidth=1, relheight=1)
def new_window(self):
self.newWindow = tk.Toplevel(self.master)
self.app = admin_login(self.newWindow)
class admin_login:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.frame.place(relwidth=1, relheight=1)
self.login_button = tk.Button(self.frame, text="Login", font=40, command=self.login)
self.login_button.pack()
self.back_button = tk.Button(self.frame, text="Exit", font=40, command=self.close_window)
self.back_button.pack()
def new_window(self):
self.newWindow = tk.Toplevel(self.master)
self.app = admin_panel(self.newWindow)
def close_window(self):
self.master.destroy()
def login(self):
self.close_window()
self.new_window()
class admin_panel:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_window)
self.quitButton.pack()
self.frame.pack()
def close_window(self):
self.master.destroy()
def main():
root = tk.Tk()
app = auto_haven(root)
root.mainloop()
if __name__ == '__main__':
main()
When you call self.login, the first thing it does is call self.close_window(). When you do that, it calls self.master.destroy(). It then calls self.new_window() which calls self.newWindow = tk.Toplevel(self.master).
Notice that you are now trying to create a new window as a child of self.master, but you've destroyed self.master so tkinter will throw an error. When you create a new window, it needs to be the child of an existing window, such as the root window.
Communication between objects
The idea is create a Toplevel window from Gui and after Toplevel closed send the data (name) from Toplevel Entry back to Gui
How object app can know whether the toplev object was destroyed?
or with other words
How can object of Gui know that the object of My_Toplevel is closed?
from tkinter import *
font1 = font=("Open Sans Standard",16,"bold")
class My_Toplevel():
def __init__(self, master=None):
self.master = master
self.toplev = Toplevel(master)
self.name = None
self.create_widgets()
def create_widgets(self):
self.entry_name = Entry(self.toplev, font=font1)
self.button_ok = Button(self.toplev, text="Ok", font=font1,
command=self.get_name)
self.entry_name.pack()
self.button_ok.pack()
def get_name(self):
self.name = self.entry_name.get()
self.toplev.destroy()
class Gui(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.pack()
self.master = master
self.label_text = Label(self, text="Foo Bar Window", font=font1)
self.label_text.pack()
self.button_toplevel = Button(self, text="Create Toplevel",
command=self.get_toplevel, font=font1)
self.button_toplevel.pack()
def get_toplevel(self):
self.my_top = My_Toplevel(self)
if __name__ == "__main__":
root = Tk()
root.title("Parent")
app = Gui(root)
root.mainloop()
You need to pass the data to the Gui instance before you destroy My_Toplevel. One way to do that is to save the name string as an attribute of the Gui instance since you pass that the master parameter when you call My_Toplevel. For example:
from tkinter import *
font1 = font=("Open Sans Standard",16,"bold")
class My_Toplevel():
def __init__(self, master=None):
self.master = master
self.toplev = Toplevel(master)
self.create_widgets()
def create_widgets(self):
self.entry_name = Entry(self.toplev, font=font1)
self.button_ok = Button(self.toplev, text="Ok", font=font1,
command=self.get_name)
self.entry_name.pack()
self.button_ok.pack()
def get_name(self):
self.master.name_data = self.entry_name.get()
self.toplev.destroy()
class Gui(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.pack()
self.master = master
self.label_text = Label(self, text="Foo Bar Window", font=font1)
self.label_text.pack()
self.button_toplevel = Button(self, text="Create Toplevel",
command=self.get_toplevel, font=font1)
self.button_toplevel.pack()
self.name_data = None
Button(self, text="show name", command=self.show_name).pack()
def show_name(self):
print("Name =", self.name_data)
def get_toplevel(self):
self.my_top = My_Toplevel(self)
if __name__ == "__main__":
root = Tk()
root.title("Parent")
app = Gui(root)
root.mainloop()
Press the "show name" button to print the name string to the console.
If you need to save more than a single string, you could append the name to a list, save it in a dictionary, etc.
If you like, you can call the Gui.show_name method just before the TopLevel window is destroyed:
def get_name(self):
self.master.name_data = self.entry_name.get()
self.master.show_name()
self.toplev.destroy()
i have written the "instruction" for my tkinter program on a .txt file and when the Instruction button is pressed, I would like a separate window to be created with the instruction copied from the text file into the output box.
I would be very grateful if anyone could help me.
this is what I have done so far but it doesn't work at all.
instructionBtn = Button(window, text="?", command=start, height = 5, width =30,fg="black", bg="lime green", font=("Comic Sans MS", 10))
instructionBtn.grid(row=29, column=1, sticky=S)
window.mainloop()
def instruction():
instructionwindow = Tk() #create window
instructionwindow.geometry("500x350")
instructionwindow.title("Instruction")
instructionwindow.configure(background='white')
instructionFile=open("Instruction.txt","r")
instruction.read
textboxOutput = Text(window, wrap="Instruction.txt", width = 150, height = 20)
textboxOutput.grid(row = 20, column = 0, columnspan=10)
instruction.mainloop()
When I want a second window, often a message box will do the trick, like so:
from Tkinter import *
from tkMessageBox import showinfo
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="QUIT", fg="red", command=frame.quit)
self.button.pack(side=LEFT)
self.instr = Button(frame, text="Instruction", command=self.instruction)
self.instr.pack(side=LEFT)
def instruction(self):
try:
with open("instruction.txt") as fp:
message = fp.read()
except IOError:
message = 'No instructions available'
showinfo("Instructions", message)
root = Tk()
app = App(root)
root.mainloop()
Or, if you prefer an OOP style:
# template: https://stackoverflow.com/a/17470842/8747
# Use Tkinter for python 2, tkinter for python 3
import Tkinter as tk
import tkMessageBox
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.button = tk.Button(
self, text="QUIT", fg="red", command=self.quit)
self.instr = tk.Button(
self, text="Instruction", command=self.instruction)
self.button.pack(side=tk.LEFT)
self.instr.pack(side=tk.LEFT)
def instruction(self):
try:
with open("instruction.txt") as fp:
message = fp.read()
except IOError:
message = 'No instruction available'
msg = tkMessageBox.showinfo("Instruction", message)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Of course, sometimes a message box isn't flexible enough, and you need to create a top-level window:
# template: https://stackoverflow.com/a/17470842/8747
# Use Tkinter for python 2, tkinter for python 3
import Tkinter as tk
import tkMessageBox
class Instruction(tk.Toplevel):
def __init__(self, parent, message, *args, **kwargs):
tk.Toplevel.__init__(self, parent, *args, **kwargs)
self.msg = tk.Message(self, text=message)
self.button = tk.Button(self, text="Dismiss", command=self.destroy)
self.msg.pack()
self.button.pack()
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.button = tk.Button(
self, text="QUIT", fg="red", command=self.quit)
self.instr = tk.Button(
self, text="Instruction", command=self.instruction)
self.button.pack(side=tk.LEFT)
self.instr.pack(side=tk.LEFT)
def instruction(self):
try:
with open("instruction.txt") as fp:
message = fp.read()
except IOError:
message = 'No instruction available'
msg = Instruction(self, message)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Can anyone help me to understand the window code? Im trying to make all the objects run in a Toplevel() function instead of a new instance of Tkinter because that doesn't work launching it from another program. Theres no window = Tk() function anywhere just init and frame.
from tkinter import *
from tkinter.simpledialog import askstring
from tkinter.filedialog import asksaveasfilename
from tkinter.messagebox import askokcancel
def text_edit() :
class Quitter(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack()
widget = Button(self, text='Quit', command=self.quit)
widget.pack(expand=YES, fill=BOTH, side=LEFT)
def quit(self):
ans = askokcancel('Verify exit', "Really quit?")
if ans: Frame.quit(self)
class ScrolledText(Frame):
def __init__(self, parent=None, text='', file=None):
Frame.__init__(self, parent)
self.pack(expand=YES, fill=BOTH)
self.makewidgets()
self.settext(text, file)
def makewidgets(self):
sbar = Scrollbar(self)
text = Text(self, relief=SUNKEN)
sbar.config(command=text.yview)
text.config(yscrollcommand=sbar.set)
sbar.pack(side=RIGHT, fill=Y)
text.pack(side=LEFT, expand=YES, fill=BOTH)
self.text = text
def settext(self, text='', file=None):
if file:
text = open(file, 'r').read()
self.text.delete('1.0', END)
self.text.insert('1.0', text)
self.text.mark_set(INSERT, '1.0')
self.text.focus()
def gettext(self):
return self.text.get('1.0', END+'-1c')
class SimpleEditor(ScrolledText):
def __init__(self, parent=None, file=None):
frm = Frame(parent)
frm.pack(fill=X)
Button(frm, text='Save', command=self.onSave).pack(side=LEFT)
Button(frm, text='Cut', command=self.onCut).pack(side=LEFT)
Button(frm, text='Paste', command=self.onPaste).pack(side=LEFT)
Button(frm, text='Find', command=self.onFind).pack(side=LEFT)
Quitter(frm).pack(side=LEFT)
ScrolledText.__init__(self, parent, file=file)
self.text.config(font=('courier', 9, 'normal'))
def onSave(self):
filename = asksaveasfilename()
if filename:
alltext = self.gettext()
open(filename, 'w').write(alltext)
def onCut(self):
text = self.text.get(SEL_FIRST, SEL_LAST)
self.text.delete(SEL_FIRST, SEL_LAST)
self.clipboard_clear()
self.clipboard_append(text)
def onPaste(self):
try:
text = self.selection_get(selection='CLIPBOARD')
self.text.insert(INSERT, text)
except TclError:
pass
def onFind(self):
target = askstring('SimpleEditor', 'Search String?')
if target:
where = self.text.search(target, INSERT, END)
if where:
print (where)
pastit = where + ('+%dc' % len(target))
#self.text.tag_remove(SEL, '1.0', END)
self.text.tag_add(SEL, where, pastit)
self.text.mark_set(INSERT, pastit)
self.text.see(INSERT)
self.text.focus()
if __name__ == '__main__':
try:
SimpleEditor(file=sys.argv[1]).mainloop()
except IndexError:
SimpleEditor().mainloop()
You have to adapt to Tkinter as it was written to be used (or write your own which is beyond me), instead of trying to pound it into some preconceived form.
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").grid(row=1, column=0)
Ot=OpenToplevels()