How would I go about writing a command that fully closes the currently open tkinter programm. There are already a ton of threads regarding this, but I have been unable to find a solution that reliably closes all open tkinter windows. In my scenario, I am adding menus to each window with the option to close the current window, as well as close the whole programm. While closing the current window always works, closing the whole programm only works sporadically.
Things I have tried:
using root.quit() frequently leads to the "Python not responding" windows error message.
using root.destroy() will close the root window, and all toplevel windows, if I have not clicked on anything in them. Once I do use a toplevel window, it will not be ended by the root.destroy() command anymore.
using mytoplevel.destroy() will reliably close the currently active toplevel window.
I have written a simplified version of the code I am using, however, the windows don't do anything here, so the destroy command will always work.
import tkinter as tk
class MainWindow(tk.Frame):
def __init__(self, master = None):
b = tk.Button(master, text="Type_1", command=self.window_type_1)
b.pack()
self.load_menu(root)
tk.Frame.__init__(self, master)
def close(self, parent):
parent.destroy()
def window_type_1(self):
top = tk.Toplevel()
top.minsize(width = 600, height = 500)
self.load_menu(top)
def load_menu(self, parent):
menubar = tk.Menu(parent, tearoff=False)
parent.config(menu=menubar)
start_menu = tk.Menu(menubar, tearoff=False)
if parent == root:
start_menu.add_command(label="close", command=lambda: self.close(parent))
menubar.add_cascade(label="File", menu=start_menu)
else:
start_menu.add_command(label="Window close", command=lambda: self.close(parent))
start_menu.add_command(label="Programm close", command=lambda: self.close(root))
menubar.add_cascade(label="File", menu=start_menu)
root = tk.Tk()
mainwindow = MainWindow(master = root)
mainwindow.mainloop()
Calling destroy on the root window is generally the right solution, and is specifically the right solution in your example program. Destroying the root window will cause mainloop to exit, so unless you have more code after the call to mainloop and/or more code running in a separate thread, the program must exit.
Related
from tkinter import *
master=Tk()
class check:
def __init__(self,root):
self.root=root
self.b1=Button(root,text="Click me",command=self.undo)
self.b2=Button(root,text="Again",command=self.click)
def click(self):
self.b1.place(relx=0.5,rely=0.5)
def undo(self):
self.b1.destroy()
self.b2.place(relx=0.2,rely=0.2)
c=check(master)
c.click()
master.mainloop()
This is my code. I get _tkinter.TclError: bad window path name ".!button" error only when I use destroy method. But I want to delete previous button when another button appears.What should I do?
What are you doing? When you click the "Click me" button (and call the self.undo method, where the self.b1 button is destroyed) and then click the "Again" button (and call the self.click method, which tries to place already destroyed self.b1 button), you get the error, that the button does not exist. Of course, it doesn't because you have destroyed it.
It looks like you meant to hide the button. If you intended to do this, then you could just use .place_forget() method (there are also .pack_forget() and .grid_forget() methods for pack and grid window managers, respectively), that hides the widget, but not destroys it, and hence you would be able to restore it again when you need.
Here is your fixed code:
from tkinter import *
master = Tk()
class check:
def __init__(self, root):
self.root = root
self.b1 = Button(root, text="Click me", command=self.undo)
self.b2 = Button(root, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
c = check(master)
c.click()
master.mainloop()
I can also give you a piece of advice about the implementation:
1) You should write the code according to the PEP8 style; classes should be named in the CamelCase.
2) You should inherit your Tkinter app class(es) either from Tk (usage is shown below) Toplevel(the same as Tk, but use ONLY for child windows), Frame class (almost the same as for Tk, but you need to pack/grid/place that Frame in a window).
3) It's better to create the widgets in a separate function (it helps while developing complex and big apps).
4) It's recommended to write if __name__ == "__main__": condition before creating the window (if you do like this, you will be able to import this code from other modules, and the window won't open in that case).
Here is an example:
from tkinter import *
class Check(Tk):
def __init__(self):
super().__init__()
self.create_widgets()
self.click()
def create_widgets(self):
self.b1 = Button(self, text="Click me", command=self.undo)
self.b2 = Button(self, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
if __name__ == "__main__":
Check().mainloop()
After you destroyed button b1 in the undo(self) function tkinter cannot access it anymore and will be confused when you try to place is somewhere in the click(self) function.
To make button b1 only disappear visually you could place it outside of the window instead of destroying it. To do so replace
self.b1.destroy()
with
self.b1.place(relx=-5, rely=0)
This will move the button b1 far to the left, where it cannot be seen.
When calling the click(self) function, the button will reappear, because it will be moved inside the window again.
i am making a GUI and in it I am keeping the root hidden. So when i close the window which is showing using the x arrow, i wont the whole program to end not just the window that can be seen. Because other wise the user will have problems when closing the program.my root is hidden,
login can be seen,
when login is closed using the red x at the top i want to close the root as well how to do that?
You can bind to the <Destroy> event on your window. Within the bound function, you can do whatever you want including destroying the root window.
In the following example, killing the Toplevel windows will destroy the root window.
import tkinter as tk
class Window(tk.Toplevel):
def __init__(self, root, label):
super().__init__(root)
self.root = root
label = tk.Label(self, text=label)
label.pack(padx=20, pady=20)
self.bind("<Destroy>", self.kill_root)
def kill_root(self, event):
if event.widget == self and self.root.winfo_exists():
self.root.destroy()
root = tk.Tk()
label = tk.Label(root, text="Root window")
label.pack(padx=20, pady=20)
w1 = Window(root, "This is a toplevel window")
root.mainloop()
The reason for checking that event.widget is self is due to the fact that functions bound to the root or toplevel window are automatically inherited by all children within that window You only want to destroy the root window when the actual toplevel window is destroyed.
I'm having trouble figuring out how to close a python tkinter window menu when clicking on the root of the menu, ie."File".
I was hoping there was just a command option available when instantiating the menu but it appears as though that is not the case. Also I know that there is an unpost method I can use to get rid of the menu but I don't know how I would trigger it on root menu click.
Edit:
Since Bryan said that my menus should be acting the same as any other menus in my os I decided to put together an example of how I'm doing menus. Maybe I am doing something wrong here but on ubuntu in every other program if I click on the root of the menu it will close it again. In this program it does not close when clicking on the root, only when clicking elsewhere on the screen does the menu close.
import tkinter as tk
import tkinter.messagebox
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("App Here")
self.window_menu = tk.Menu(self)
self.filemenu = tk.Menu(self.window_menu, tearoff=0)
self.build_window_menu()
self.config(menu=self.window_menu)
def build_window_menu(self):
self.window_menu.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_command(label='alert', command=self._handle_menualert)
def _handle_menualert(self):
tk.messagebox.showwarning(
"Menu Stuff",
"I am menu alert!"
)
if __name__ == '__main__':
app = App()
app.mainloop()
I'm trying to create a screen "curtain" which blocks parts of the screen except for the mouse cursor's vicinity.
On windows, using root.wm_attributes("-topmost", "true") keeps the window on top, even if I focus on another app, perfectly. However, upon running the code on MacOS, if the focus for the window is lost, it will not keep itself on topmost.
What would be the MacOS equivalent to -topmost window manager attribute which will always keep the window on top, regardless of focus?
import tkinter as tk
class TransparentWindow(tk.Toplevel):
"""
This class is just a Toplevel window.
"""
def __init__(self, background="white", opacity=0.7):
super(TransparentWindow, self).__init__()
#self.master = master
self.configure(background=background)
self.overrideredirect(True)
self.wm_attributes("-alpha", opacity)
self.wm_attributes("-topmost", "true")
self.lift()
if __name__ == '__main__':
root = tk.Tk()
TransparentWindow()
root.mainloop()
Running this code in a High Sierra Virtual Machine resulted in the Toplevel not constantly being on top when another window is selected.
On Mac OS using overrideredirect(True) disables a lot of stuff like bind, Button presses and some events, honestly I don't know why exactly. (If anyone knows please comment). At least on my Mac I have this problem, I've read and seen that not all of Mac users have this problem.
So this is why root.wm_attributes("-topmost", "true") is not working. But don't worry I got a workaround.
From your code I can tell that you want a borderless window, here is how I do it with all bindings and event working still.
I first put overrideredirect(True) then in the next line
overrideredirect(False)
Also you don't need root.lift() in this case.
Ok try this code and see if the button press normally.
Sample
import tkinter as tk
root = tk.Tk()
root.overrideredirect(True)
# root.overrideredirect(False) # Uncomment and try again.
tk.Button(root, text="Borderless").pack()
root.wm_attributes("-topmost", "true")
root.wm_attributes("-alpha", 0.7)
root.wm_attributes("-topmost", "true")
# Doesn't matter if you use lift() or not with the use of root.overrideredirect(False) as well
root.lift()
root.mainloop()
I hope this helped you.
Here is your code which worked exactly you want (At least on my Mac).
import tkinter as tk
class TransparentWindow(tk.Toplevel):
"""
This class is just a Toplevel window.
"""
def __init__(self, background="white", opacity=0.7):
super(TransparentWindow, self).__init__()
#self.master = master
self.configure(background=background)
self.overrideredirect(True)
self.overrideredirect(False)
self.wm_attributes("-alpha", opacity)
self.wm_attributes("-topmost", "true")
# self.lift()
if __name__ == '__main__':
root = tk.Tk()
TransparentWindow()
root.mainloop()
How do you make a button open a duplicate window of the main page in a new window?
What would the python code be to do this with Tkinter?
For clarification, in notepad++ when you right click on the tab that you are in, it shows you a whole menu.
Then, there is a dropdown menu with an option that says, open in a new instance or move to a new instance.
This would then open the tab you selected into a new tab/window.
I haven't tried anything yet but I did look up about how to do this type of thing and there were no results on what exactly I wanted to do.
Can you try to show me an example of how what I need to do can be done?
Code:
Found Here
The simplest solution is to make your window be a subclass of Toplevel. Then, each time you need a new window, create a new instance of the class.
You can then leave the root window blank and hide it so that you only see your custom windows. It's important to make sure that you destroy the root window after the user has deleted the last application window, or else you'll end up with an invisible window that will be hard to kill.
Here is a bit of a contrived example:
import tkinter as tk
class AppWindow(tk.Toplevel):
def __init__(self, root):
tk.Toplevel.__init__(self, root)
self.root = root
menubar = tk.Menu(self)
windowMenu = tk.Menu(menubar)
windowMenu.add_command(label="New Window", command=self.new_window)
windowMenu.add_command(label="Quit", command=root.destroy)
menubar.add_cascade(label="Window", menu=windowMenu)
self.configure(menu=menubar)
self.text = tk.Text(self)
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
self.text.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.text.pack(side="left", fill="both", expand=True)
# call a function to destroy the root window when the last
# instance of AppWindow is destroyed
self.wm_protocol("WM_DELETE_WINDOW", self.exit_on_last_window)
def new_window(self):
AppWindow(self.root)
def exit_on_last_window(self):
"""Destroy the root window when no other windows exist"""
self.destroy()
if not any([window.winfo_exists() for window in self.root.winfo_children()]):
self.root.destroy()
def quit(self):
self.root.destroy()
# create the root, then hide it. The app will create its
# own windows as Toplevels
root = tk.Tk()
root.withdraw()
# create the first window, then start the event loop
AppWindow(root)
tk.mainloop()