My Code opens a window with a button. When the button gets clicked a toplevel window is created and the root window gets destroyed. When the button on the toplevel window gets clicked a messagebox opens. I want the the Toplevel Window to be closed when the user presses the ok button of the messagebox.
Pressing Ok causes the TypeError: destroy() missing 1 required positional argument: 'self'
I really don't understand why it dosn't work since the toplevel window gets passed as an argument to the destroy method.
import tkinter as tk
from tkinter import messagebox
def main():
root = tk.Tk()
root.title("Hauptmenü")
mainmenue(root)
root.mainloop()
def mainmenue(root):
button_rennen = tk.Button(root, text="New Window", width=20,
command=lambda: call_window(root))
button_rennen.pack()
def call_window(root):
root.destroy()
rframe = tk.Toplevel
button = tk.Button(text="Wette platzieren",
command=lambda: question(rframe))
button.pack()
def question(rframe):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
rframe.destroy()
main()
def call_window(root):
root.destroy()
rframe = tk.Tk()
button = tk.Button(rframe, text="Wette platzieren",
command=lambda: question(rframe))
button.pack()
replace toplevel with Tk window and it works fine.
Related
Is there any way to disable all windows when a messagebox popup is created in tkinter?
Here's the code:
from tkinter import *
from tkinter import messagebox
def show():
messagebox.showinfo("Test Popup", "Hello world")
root = Tk()
root.title("Main Window")
root.geometry("500x500")
toplevel = Toplevel(root)
toplevel.title("Toplevel Window")
toplevel.geometry("300x300")
show_button = Button(root , text = "Show popup" , command = show)
show_button.place(x = 200 , y = 200)
mainloop()
Here when the messagebox pops up, I don't want the user to be able to interact with any other Tk or Toplevel windows until that popup is destroyed.
(I tried using the parent attribute of the messagebox, but it only disables one window.)
Is there any way to achieve this in tkinter?
It would be great if anyone could help me out.
I don't think it is possible to prevent all interactions with the windows like moving them around (except if you use .overrideredirect(True) which will make the window decoration disappear and the widow will stop being handled by the window manager).
However, it is possible to
prevent the toplevel to come on top of the popup
disable the "close" button of both the root window and toplevel when the popup is displayed
For both I use the following general idea in show():
def show():
# modify state of root and toplevel to make them less interactive
# ...
messagebox.showinfo("Test Popup", "Hello world", parent=root)
# put root and toplevel back in their normal state
# ...
For 1. I use root.attributes('-topmost', True) before displaying the popup, which inherits this property from root and therefore will stay on top of toplevel.
For 2. I use window.protocol("WM_DELETE_WINDOW", lambda: quit(window)) which calls quit(window) when the user clicks on the close button of window. In quit(), I check whether the popup is opened before destroying the window:
def quit(window):
if not popup:
window.destroy()
popup is a global variable which value is changed in show().
Full code:
import tkinter as tk
from tkinter import messagebox
def quit(window):
if not popup: # destroy the window only if popup is not displayed
window.destroy()
def show():
global popup
popup = True
root.attributes('-topmost', True)
messagebox.showinfo("Test Popup", "Hello world", parent=root)
root.attributes('-topmost', False)
popup = False
root = tk.Tk()
popup = False
root.protocol("WM_DELETE_WINDOW", lambda: quit(root))
root.title("Main Window")
root.geometry("500x500")
toplevel = tk.Toplevel(root)
toplevel.protocol("WM_DELETE_WINDOW", lambda: quit(toplevel))
toplevel.title("Toplevel Window")
show_button = tk.Button(root, text="Show popup", command=show)
show_button.pack()
root.mainloop()
You can probably add some more stuff in show(), e.g. .resizable(False, False) if you don't want the user to be able to resize the windows when the popup is displayed.
After experimenting for a few days, I finally found the solution.
The basic idea here is to get all the child widgets of a window, check whether the child is an instance of Tk or Toplevel, and apply the -disabled attribute to them.
Here's the implementation:
from tkinter import *
from tkinter import messagebox
def disable_windows(window):
for child in window.winfo_children(): # Get all the child widgets of the window
if isinstance(child, Tk) or isinstance(child, Toplevel): # Check if the child is a Tk or Toplevel window so that we can disable them
child.attributes('-disabled', True)
disable_windows(child)
def enable_windows(window):
for child in window.winfo_children(): # Get all the child widgets of the window
if isinstance(child , Tk) or isinstance(child , Toplevel): # Check if the child is a Tk or Toplevel window so that we can enable them
child.attributes('-disabled' , False)
enable_windows(child)
def increase_popup_count():
global popup_count
popup_count += 1
if popup_count > 0: # Check if a popup is currently active so that we can disable the windows
disable_windows(root)
else: # Enable the windows if there is no active popup
enable_windows(root)
def decrease_popup_count():
global popup_count
popup_count -= 1
if popup_count > 0: # Check if a popup is currently active so that we can disable the windows
disable_windows(root)
else: # Enable the windows if there is no active popup
enable_windows(root)
def showinfo(title, message): # A custom showinfo funtion
increase_popup_count() # Increase the 'popup_count' when the messagebox shows up
messagebox.showinfo(title , message)
decrease_popup_count() # Decrease the 'popup_count' after the messagebox is destroyed
def show():
showinfo("Test Popup", "Hello world")
root = Tk()
root.title("Main Window")
root.geometry("500x500")
popup_count = 0
toplevel = Toplevel(root)
toplevel.title("Toplevel Window")
toplevel.geometry("400x400")
toplevel_2 = Toplevel(toplevel)
toplevel_2.title("Toplevel Window of Another Toplevel")
toplevel_2.geometry("300x300")
show_button = Button(root , text = "Show popup" , command = show)
show_button.place(x = 200 , y = 200)
mainloop()
I am on Ubuntu 20.04 and struggling with making iconify() and deiconify() work for a popup window. Currently the popup window will not deiconify() after having been iconified. Here is a minimal example:
import tkinter as tk
class PopupWindow(tk.Toplevel):
""" Show summary
"""
def __init__(self, root):
super().__init__(master=root)
self.root = root
self.geometry("600x400")
self.title("Summary")
self.protocol("WM_DELETE_WINDOW", self.close_window)
button = tk.Button(
self, text="Close", command=self.close_window)
button.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
self.focus_force()
self.grab_set()
def close_window(self):
self.iconify()
#self.withdraw()
#self.transient()
#self.root.grab_set()
#self.root.focus_force()
class MainWindow(tk.Tk):
""" The main window
"""
def __init__(self):
tk.Tk.__init__(self)
self.popup_window = None
self.configure_window()
def open_popup_window(self):
""" Opens the popup window
"""
if self.popup_window is None:
self.popup_window = PopupWindow(self)
else:
self.popup_window.deiconify()
#self.popup_window.lift()
self.popup_window.focus_force()
#self.popup_window.grab_set()
def configure_window(self):
""" Configure the main window
"""
self.geometry("600x400")
self.title("Cinema bookkeeper")
self.bind_all('<Control-w>', lambda e: self.destroy())
button = tk.Button(
self, text="Open popup window", command=self.open_popup_window)
button.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
def main():
window = MainWindow()
window.mainloop()
main()
When I click the "Open popup window" button a second time after it first has been iconified, nothing happens. Expected behavior was that the popup window would reappear and get focus. I can make it work if I use grab_set() but I would like not make the popup window modal such that both windows still can receive input at the same time.
The grab_set method routes all the events of the application to the specified widget.
Since you have called self.grab_set() on the popup window, the main window will refuse to take any event (the button is never clicked to begin with) and so the program doesn't work.
Removing that would make it work.
I want to close two windows at the same time when user click the Start button, the new window will pop-up and when the user clicks the Exit button on the Second pop-up window than both the window should Close at a time.
I know that for a different window I have to create a separate function to exit windows But I want to close more than one window with a single click.
I'm using python 3.7!
import tkinter
def NewWindow():
def qExit():
root.destroy()
root = tkinter.Tk()
root.title("New Window")
newButton = tkinter.Button(root, text=" Click here to Exit:",
command=qExit)
newButton.pack()
root.geometry("300x200")
root.mainloop()
Window = tkinter.Tk()
Window.title("hello")
eButton = tkinter.Button(Window, text="Start", command=NewWindow)
eButton.pack()
Window.geometry("200x200")
Window.mainloop()
You shouldn't call tkinter.Tk() more than once in a tkinter application. Call Toplevel() if you want to create a new window.
You also generally don't need to call mainloop() more than once.
To close both the new window and the main window, you can pass the latter to the former when you create it, and then destroy() that in your qExit() function (as well as the new window itself).
Note I changed some of the function and variable names to conform more to the PEP 8 - Style Guide for Python Code guidelines.
import tkinter
def makeWindow(parent):
def qExit():
newWindow.destroy()
parent.destroy()
newWindow = tkinter.Toplevel()
newWindow.geometry("300x200")
newWindow.title("New Window")
newButton = tkinter.Button(newWindow, text=" Click here to Exit",
command=qExit)
newButton.pack()
root = tkinter.Tk()
root.title("hello")
eButton = tkinter.Button(root, text="Start", command=lambda: makeWindow(root))
eButton.pack()
root.geometry("200x200")
root.mainloop()
A simple solution would be to just do exit() to stop the program, which will close all windows. alternatively you can make a list of all open window objects and call destroy on all of them.
No need description
def qExit():
root.destroy()
Window.destroy()
When I make a button that closes the current window and opens another, the current window doesn't close.
from tkinter import *
root = Tk()
def new_window():
root.quit()
new_window = Tk()
new_window.mainloop()
Button(root, text="Create new window", command=new_window).pack()
root.mainloop()
(This isn't my program, it's just an example)
You should be able to do it like this:
import tkinter as tk
root = tk.Tk()
def new_window():
root = tk.Tk()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
This will literally keep opening the exact same window with a button. The lambda allows you to call multiple functions. By calling .destroy() on your root window, it destroys your window, but doesn’t stop the program. Then you create a new root window with your function.
You can use this technique on your actual script.
I created a tkinter Toplevel window for my application and later in the program destroyed it but after destroying the window the program doesnt get executed further and get struck there itself doing nothing . Here is the code that I used :-
#login.py
from tkinter import *
class gui:
def __init__(self):
#does something
def login(self):
self.winLogin.destroy()
def guilogin(self):
self.winLogin = Toplevel()
btn = Button(self.winLogin,command=self.login,text='asd')
btn.pack()
self.winLogin.mainloop()
#main.py
import login
from tkinter import *
main = Tk()
a = login.gui()
a.guilogin()
if True:
#some code and this part doesnot get executed
main.mainloop()
else:
main.destroy()
I run main.py file and the code get struck and do nothing before the if part . I tottaly have no idea whats wrong . Pls. Help!
As furas said in the comments, you should not call mainloop on the toplevel, instead use grab_set to disable the main window and wait_window to wait for the toplevel to be closed:
from tkinter import Tk, Toplevel, Button
def login():
top = Toplevel(root)
Button(top, text="Quit", command=top.destroy).pack()
top.grab_set() # deactivate the main GUI while top is opened
root.wait_window(top) # wait for top to be closed before doing the rest
print("logged in")
root = Tk()
Button(root, text="login", command=login).pack()
root.mainloop()