I have code for selecting backup that should be loaded. It opens Toplevel window and lets you select one of the backups to load. When this window opens, I would like to block input to original window, so the only way to get back to original window is by closing the new Toplevel window.
The part of code that I hoped would work:
from tkinter import *
class BackupsGui:
def __init__(self, parent):
top = Toplevel()
self.top = top
Some more code and __init__ ending with:
top.update_idletasks()
top.overrideredirect(True)
top.mainloop()
or:
top.transient(parent)
top.mainloop()
Niether code part appears to change Toplevel interaction in any way, nor does changing if top.mainloop() precedes top.transient() or top.update_idletasks().
What do I miss?
transient and overrideredirect have nothing to do with event handling. If you want to block all input except for the toplevel, you need to call grab_set on the toplevel window. This will cause all events to be sent to that window.
Run the following code, and notice that if you don't check the box, you can continue to create new windows and change the value of the checkbox. Once checked, the next window grabs all events, preventing you from interacting with the other windows.
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.do_grab = tk.BooleanVar()
cb = tk.Checkbutton(self, text="New window grabs all events",
variable=self.do_grab, onvalue=True, offvalue=False)
cb.pack()
new_button = tk.Button(self, text="New window", command=self.on_click)
new_button.pack()
def on_click(self):
self.top = tk.Toplevel(self)
button = tk.Button(self.top, text="dismiss", command=self.top.destroy)
do_grab = self.do_grab.get()
if do_grab:
label = tk.Label(self.top, wraplength=200,
text="This window grabs all events")
else:
label = tk.Label(self.top, wraplength = 200,
text="This window does NOT grab all events")
label.pack(fill="x")
button.pack()
if do_grab:
self.top.grab_set()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Related
I am trying to call a new window by pushing the button of an existing window. The original window should close when the new window is being created. When I push the button the new window will show up as expected but additionally a blank window will appear. Using tk.Tk() or tk.Toplevel() will lead to the same result.
Later in the program I want to destroy the created window again. When using tk.Tk() closing the blank window by mouse will create an error "application has been destroyed" when the destroy() method gets applicated to the new window.
import tkinter as tk
from tkinter import messagebox
def main():
root = tk.Tk()
root.title("Hauptmenü")
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20, command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
self.destroy()
root2 = tk.Tk()
Bet = BetFrame(root2)
Bet.pack()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button = tk.Button(text="Wette platzieren",
command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
main()
I am creating a new class for every new window since the original program should return some variables.
I know that there are already many questions to this topic but for me none of these seemed quite to fit and helped to find the solution for my problem. I am grateful for any help!
Look at this:
import tkinter as tk
from tkinter import messagebox
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20,
command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
# `self.master` is the window
Bet = BetFrame(self.master)
Bet.pack()
self.destroy()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
# You need to tell the button that its master should be this BetFrame
# If you don't it will assume you mean the window so
# `self.destroy()` isn't going to destroy the button.
self.button = tk.Button(self, text="Wette platzieren", command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
def main():
root = tk.Tk()
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
main()
Your code is great but it was 2 mistakes:
Instead of creating a new window is it better to reuse the old one.
When you create your button in your BetFrame class, you don't pass anything for the master argument (the first argument). That is why the button isn't destroyed when you destroy the BetFrame object using self.destroy()
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'm currently creating a login system which connects to a main application. The user uses the log in system to log in through a series of window, and after this is done, the log in window should close and the main program should open.
I've created a small example of my project. The issue I'm getting is that StringVar() doesn't seem to work in the second window.
Code for the first window:
# Python program to create a window linking to the second window
from tkinter import *
import tkinter as tk
import test2
class Window(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
frame = Frame(self)
frame.grid()
# creates a button which closes this window and opens the next
button2 = Button(frame, text='next frame', command=lambda: self.open_window())
button2.grid()
def open_window(self):
# runs the 'openWindow' function from the second file named 'test2'
test2.openWindow(app, test2)
if __name__ == '__main__':
app = Window()
app.mainloop()
Code for the second window:
# Second window named 'test2' which is displayed after the button in the first window is pressed
from tkinter import *
import tkinter as tk
def openWindow(app, window):
Application = window.Window2()
app.withdraw() # closes the first window
Application.mainloop() # opens the second window
class Window2(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
frame = Frame(self)
frame.grid()
self.var = StringVar() # creates the StringVar()
self.var.set("default")
# creates an entry which is linked to self.var
entry = Entry(frame, textvar=self.var, width=50, bg='lightgrey', justify=LEFT)
entry.grid(padx=16, ipady=20)
# this button should print the contents of the entry widget
button = Button(frame, text='print', command=lambda: self.print_var())
button.grid()
def print_var(self):
# prints value of self.var
print(self.var.get())
The problem I get is that when I press the button in the second window, it doesn't print the actual contents of the entry widget. I think this means that the StringVar() isn't working properly so doesn't link to the entry widget, but I'm not sure why this is happening.
For some reason, closing a tkinter popup window with a button requires you to update the window to close it (otherwise it will just sit there), but calling update to close the window raises an exception.
I've tried the following, which works but raises an exception, which I cant have in the final product:
Button1 = ttk.Button(popupWindow, text="Close", command=popupWindow.destroy)
Button1.pack(expand = Y)
while popupWindow:
time.sleep(0.1)
popupWindow.update()
I've also tried the obvious try/except method:
Button1 = ttk.Button(popupWindow, text="Close", command=popupWindow.destroy)
Button1.pack(expand = Y)
while popupWindow:
time.sleep(0.1)
try:
popupWindow.update()
except:
pass
This just causes the program to hang and become unresponsive, as if it's waiting for popupWindow.update() to be called. Is there a way to just silence the exception that's raised?
You should not use a while loop, sleep, and update to wait for a button click or to wait for a window to go away. Those are the root cause of the exception you're seeing.
Tkinter has functions specifically for that purpose. By using one of these features you don't have to try to catch an exception since no exception will be thrown.
In your case, it appears you're wanting to wait for a widget to be destroyed. You can use tkinter's 'wait_window` function which does exactly that -- it waits for a window to be destroyed.
Here's a contrived example of a popup window. When you create it, it centers a new window on its parent, then waits until the window is destroyed. This example uses a Frame as the base, but you could also use Toplevel or Canvas or any other widget.
import tkinter as tk
class PopupWindow(tk.Frame):
def __init__(self, parent, message):
tk.Frame.__init__(
self, parent,
borderwidth=2, relief="raised",
background="bisque",
)
label = tk.Label(self, text=message, bg=self.cget("background"))
ok_button = tk.Button(self, text="Ok", command=self.destroy)
ok_button.pack(side="bottom", pady=10)
label.pack(side="top", padx=20, pady=20)
# center this widget on parent window
self.place(relx=.5, rely=.5, anchor="center")
# wait until the window has been destroyed
self.wait_window(self)
Here's an example of using this widget:
import tkinter as tk
class Example():
def __init__(self):
self.root = tk.Tk()
self.root.geometry("400x400")
button = tk.Button(self.root, text="Do Something", command=self.do_something)
button.pack(side="top")
self.root.mainloop()
def do_something(self):
print("doing something...")
print("waiting...")
popupWindow = PopupWindow(self.root, "Click button to continue")
print("done waiting...")
ex = Example()
I am working on code in tkinter python v3.7 where I want to open a new window which has same functionalities like original window. How can I do that?
While searching for solution I came across function naming Toplevel which creates new tkinter window. But this new window is completely new, It doesn't have functionalities(Button, geometry size in my case) which were provided in original one.
from tkinter import *
class TextPad:
def new_window(self):
top = Toplevel()
def __init__(self, master):
master.title('Text Pad')
master.geometry('400x400')
self.button = Button(master, text='Press',
command=self.new_window)
self.button.pack()
root = Tk()
t = TextPad(root)
root.mainloop()
My original window has geometry size of '400x400' and It has 'button', I want to open a new window having this functionalities.
I assume you want two (or more) windows at the same time.
If you want identical window then use again TextPad()
but this time use Toplevel() instead of Tk()
def new_window(self):
top = TextPad(Toplevel())
and don't run second mainloop()
If you want replace first window by new window - to have alwasy only one window - then you would have to destroy old window and then create new one using TextPad() with Tk() and mainloop()
But it would need to use self.master instead of master to have access to master in method new_window
from tkinter import *
class TextPad:
def new_window(self):
self.master.destroy()
root = Tk()
t = TextPad(root)
root.mainloop()
def __init__(self, master):
self.master = master
self.master.title('Text Pad')
self.master.geometry('400x400')
self.button = Button(self.master, text='Press',
command=self.new_window)
self.button.pack()
root = Tk()
t = TextPad(root)
root.mainloop()