Closing multiple toplevels in tkinter - python

I have created a tkinter application where the user can make multiple toplevel windows and have the option of closing them from inside the toplevel. I would like to make a button on the main window that closes all toplevel windows. How would I do this? Is there a way to do this without lists? If these toplevels are parts of classes is there also a way to call a function present in all of them?

Here's how to do the first part of your question about making a button in the main window to delete all the Toplevels without making a list of them. This works by using the universal winfo_children() widget method to find all the child widgets of the root (main) window.
It's unclear to me what you meant about calling a function present in all of them — Toplevel widgets are instances of a predefined tkinter class which supports a predefined set of methods — and you can call them the same way the sample code below does with child.destroy().
import tkinter as tk
root = tk.Tk()
root.title('Main')
root.geometry('200x100')
def close_all(master):
for child in master.winfo_children():
if isinstance(child, tk.Toplevel):
child.destroy() # Call method.
button = tk.Button(root, text=f"Close Toplevels",
command=lambda master=root: close_all(master))
button.pack()
for i in reversed(range(4)): # Create them bottom to top.
toplevel = tk.Toplevel()
toplevel.title(f'Toplevel {i+1}')
toplevel.geometry('200x75')
toplevel.lift()
button = tk.Button(toplevel, text="Close me", command=toplevel.destroy)
button.pack()
root.mainloop()

Related

Entanglement between Tkinter Checkbuttons

Hey so i'm making a program that has a checkbutton on the main window and a toplevel window that has one aswell. the problem is that for some reason the toplevel checkbutton affects the state of the main checkbutton, or the main checkbutton mimics the top level one (if you check/uncheck the toplevel one, the main one checks/unchecks aswell). Here's an example code which displays the problem:
import tkinter as tk
def toplevel():
top = tk.Toplevel()
top.geometry('200x50')
top_chekbutton = tk.Checkbutton(top, text='top')
top_chekbutton.pack()
top.mainloop()
main = tk.Tk()
main.geometry('200x50')
open_top = tk.Button(main, text='open top', command=toplevel)
main_checkbutton = tk.Checkbutton(main, text='main')
main_checkbutton.pack()
open_top.pack()
main.mainloop()
i didn't define the state variables because they don't seem to be the source of the problem. i'm using python 3.7.7 and tkinter 8.6 on win10.
plz help :(
As a general rule of thumb, every instance of Checkbutton should have a variable associated with it. If you don't, a default value will be used that is identical for all Checkbuttons. All widgets that share the same variable will display the same value.
You can verify this yourself by printing out the value of top_chekbutton.cget("variable") and main_checkbutton.cget("variable"). In both cases the value is "!checkbutton" (at least, with the version of python I'm using).
So, assign a variable for your checkbuttons, such as a BooleanVar, IntVar, or StringVar.
main_var = tk.BooleanVar(value=False)
main_checkbutton = tk.Checkbutton(main, text='main')

Closing a window in python 3.7.2 with tkinter

I've made a program in python with Tkinter that allows you to free draw and choose different colors. I decided to make a button that would close the window instead of clicking the exit button in the top right corner. My question is how do I make the window close when the button is pressed?
If you are using a main loop for your application, then you can use the .destroy() method to release all the resources associated with the window and close the application. You call this method within the command function for your button like so:
from tkinter import *
root = Tk()
frame = Frame(root)
frame.pack(side=LEFT)
button = Button(frame, text="Exit", command=exit)
button.pack()
root.mainloop()
def exit():
root.destroy()
That should close your window. Optionally, the destroy() method may also be used at the end of your main loop if the X button of your application won't close the window immediately.
See these examples for more info:
http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.destroy-method
http://effbot.org/tkinterbook/tkinter-hello-again.htm

Why is Tkinter Toplevel object being destroyed?

In the code below, tk is not the parent of the Toplevel object that is created by the function launch(). However, when I destroy tk using tk.destroy(), the Toplevel window vanishes.
Is the Toplevel widow being destroyed? If so, how is Toplevel.destroy() being called?
from tkinter import *
def launch():
Toplevel()
tk = Tk()
frame = Frame(tk, relief="ridge", borderwidth=2)
frame.pack(fill="both", expand=1)
label = Label(frame, text="Hello, World")
label.pack(fill=X, expand=1)
button1 = Button(frame, text="Exit", command=tk.destroy)
button2 = Button(frame, text="Launch", command=launch)
button1.pack(side="bottom")
button2.pack(side="bottom")
tk.mainloop()
What keeps your application running is the mainloop of the Tk instance, which is the parent of all widgets. When you destroy it, all the widgets are also destroyed.
Keeping in mind that for each Tk instance, there's an associated Tcl interpreter, I will try to give a more detailed answer on what happens when you close a window, based on the docs strings of the Tk and associated classes and methods of the tkinter module.
Tk derives from 2 classes: Misc and Wm. In the Misc class, you can find the interface and the documentation for the quit method:
def quit(self):
"""Quit the Tcl interpreter. All widgets will be destroyed."""
self.tk.quit()
You can find under the destroy method of the Tk class the following:
def destroy(self):
"""Destroy this and all descendants widgets. This will
end the application of this Tcl interpreter."""
The destroy method in the Tk class calls also, at certain point, the destroy method of the Misc class, and there you can find also another documentation:
def destroy(self):
"""Internal function.
Delete all Tcl commands created for
this widget in the Tcl interpreter."""
Which does not say that also the Tcl interpreter is stopped (like in the quit method described above).
When constructing a Tk instance, a method called _loadtk is called. In this method, it is set the protocol when the Tk window is closed:
self.protocol("WM_DELETE_WINDOW", self.destroy)
as you can see, destroy (and not quit) is associated with the closing event of the window.
This all means that when you close the window, the Tk instance and all its children are destroyed, but the Tcl interpreter is not stopped.
Tkinter.Tk is the big poppa granddaddy of all tkinter windows. It runs the logic and communicates with the OS. When it goes -- they all go.

omitting Toplevel's parent argument

I was checking out Toplevel of tkinter. From what I've seen from effbot I can omit its parent argument.
1- When I only use Toplevel itself (commenting out root), it creates its own parent I believe since two windows appear and only destroys one after clicking button.
2- If I don't comment out Tk(), it works fine. Two windows, one root - one toplevel and destroys toplevel.
3- If I interchange root and toplevel, first toplevel creates two again(like in first case), then root will create another so three windows will appear and only toplevel gets destroyed.
import tkinter as tk
#root = tk.Tk()
top = tk.Toplevel()
#root.title("Foo")
top.title("Bar")
top.geometry("300x100")
tk.Button(top, text = "Destroy", command=top.destroy).pack()
top.mainloop()
Question is, is there a way to create toplevel before Tk() and get only one window or access its parent and destroy it?
p.s. I found these two questions Toplevel in Tkinter: Prevent Two Windows from Opening && tkinter child window opens two windows?. First question is in 2nd case which is not what I want, and second question has no answer yet and his problem kind of not reproducable.
Also, I tried to get its master value -to destroy is manually- like this but seems like that value is not stored in dictionary where options are stored.
btn = tk.Button(top, text = "Destroy", command=top.destroy)
btn.pack()
print (btn["text"])
>>> Destroy
print (btn["master"])
>>> _tkinter.TclError: unknown option "-master"
It's not that Toplevel creates it's own parent, any widget will create a root window if you don't create one first. There simply must be a root window before any other widget can exist -- that's why it's called a root window. So, to answer your specific question, no, there is no way to create an instance of Toplevel without creating a root window first.

Python Tkinter Toplevel

I am working on a program that requires multiple windows, and the first one to appear is the login window, I used the Toplevel widget in order to make other windows its children, but this code keeps showing two windows instead of one.
from Tkinter import Frame, Toplevel
from ttk import Label, Entry, Button
class loginWindow(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.title("Title")
self.frame = Frame(self)
self.frame.pack()
self.__make_layout()
self.mainloop()
def __make_layout(self):
self.frame.user_name_label = Label(text="User name:")
self.frame.user_name_text = Entry()
self.frame.user_name_label.grid(row=0, column=0)
self.frame.user_name_text.grid(row=0, column=1)
self.frame.password_label = Label(text="Password:")
self.frame.password_text = Entry()
self.frame.password_label.grid(row=1, column=0)
self.frame.password_text.grid(row=1, column=1)
self.frame.login_button = Button(text="Login")# , command=self.__create_window)
self.frame.login_button.grid(row=2, column=0, columnspan=2)
if __name__ == '__main__':
win1 = loginWindow()
All of the widgets created in _make_layout are created without a parent. This means they're children of the default root. You need to pass a parent to each of them, the same way you do to the Frame. Like this:
self.frame.user_name_label = Label(self.frame, text="User name:")
self.frame.user_name_text = Entry(self.frame)
# etc.
When I run your exact code, I don't get a second window, on any platform I try. The closest I get is on OS X, where an entry for the default root window appears in the Window menu, but the window itself still doesn't appear and the widgets all end up on the Toplevel (although not on the Frame where you wanted them). But it certainly would be legal for Tkinter to show a second window here, and put some or all of your widgets on it.
This must be a platform dependent issue, since abarnert isn't having issues with multiple windows. I use OS X with XQuartz and the following code gives me two windows:
from Tkinter import Toplevel, Tk
Toplevel().mainloop()
However, this code gives me one window:
from Tkinter import Toplevel, Tk
Tk().mainloop()
I believe your first window should be declared Tk() and subsequent windows should be Toplevel().

Categories

Resources