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.
Related
I have a script, and woult like to have some imputs, outputs (as in the terminal) and a start running script.
How can I do this?
this is what I have for now:
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
exitButton = Button(self, text="Run", command=self.clickExitButton)
exitButton.place(x=0, y=0)
def clickrunButton(self):
run() #this doesnt work
root = Tk()
app = Window(root)
# set window title
root.wm_title("Tkinter window")
# show window
root.mainloop()
You have to place your app in the root, for example with pack(). You also have to change the name of the function, because it doesn't match the one you give to the button command.
from tkinter import *
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.pack() # Window is packed in its master.
exitButton = Button(self, text="Run", command=self.clickrunButton)
exitButton.pack()
def clickrunButton(self):
self.run() # Now this work
def run(self):
print('Something')
root = Tk()
app = Window(root)
# set window title
root.wm_title("Tkinter window")
# show window
root.mainloop()
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'm working in Python 3.5 and TKinteer. Inside of a text widget, I have created a context menu that appears when the user right-clicks. However, when I try and create the commands I want (cut, copy, paste), the commands seem to have no effect.
The relevant code is as follows:
from tkinter import *
class Application:
def __init__(self,master):
self.master = master
self.initUI()
def initUI(self):
root.title("Simple Text Editor")
scrollBar = Scrollbar(root)
self.textPad = Text(root, width=100, height=100, wrap='word',
yscrollcommand=scrollBar.set,
borderwidth=0, highlightthickness=0)
scrollBar.config(command=self.textPad.yview)
scrollBar.pack(side='right', fill='y')
self.textPad.pack(side='left', fill='both', expand=True)
class PopupMenu:
def __init__(self, master, *args, **kwargs):
self.popup_menu = Menu(root, tearoff=0)
self.popup_menu.add_command(label="Cut",
command=lambda: app.textPad.event_generate('<Control-x>'))
self.popup_menu.add_command(label="Copy",
command=lambda: app.textPad.event_generate('<Control-c>'))
self.popup_menu.add_command(label="Paste",
command=lambda: app.textPad.event_generate('<Control-v>'))
app.textPad.bind("<Button-3>", self.popup)
self.popup_menu.bind("<FocusOut>",self.popupFocusOut)
def popup(self, event):
self.popup_menu.post(event.x_root, event.y_root)
self.popup_menu.focus_set()
def popupFocusOut(self, event=None):
self.popup_menu.unpost()
root = Tk()
app = Application(root)
popupMenu = PopupMenu(root)
root.mainloop()
You don't want to generate <Control-x>, etc. Instead, generate the virtual events <<Cut>>, <<Copy>> and <<Paste>>.
I have a problem that my top window is not minimize able when i use a grab_set() on the main window.
Here is a code example:
import Tkinter as tk
class mainApView(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
self.master = master
self.master.title("Mainwindow")
self.master.geometry("300x100")
self.frame = tk.Frame(self.master)
self.button = tk.Button(self.frame,text="create top level", command=self.createTopLevel)
self.button.pack()
self.frame.pack()
def createTopLevel(self):
popupWindow = tk.Toplevel(self.master)
# this function disables the minimize button
self.master.grab_set()
newTopLevel(popupWindow)
class newTopLevel():
def __init__(self,master):
self.master = master
self.master.title("New Top Level Window")
self.master.geometry("300x100")
self.frame = tk.Frame(self.master)
self.button = tk.Button(self.frame, text="useless button")
self.button.pack()
self.frame.pack()
def main():
root = tk.Tk()
app = mainApView(root)
root.mainloop()
if __name__ == '__main__':
main()
Is there a workaround? Or are the similar functions like grab_set?
Or how can i override the minimize button?
EDIT My goal is that the minimize button from the top window works and the mainwindow is disabled
I'm having a trouble when i open a secondary window. Now I'm just creating a toplevel window with a button and I need to open the same secondary window If i click the button (not generate a new instance).
Which is the better way to generate single secondary window and not generating a new window instance?
I leave the code that I'm actually working on:
import tkinter
class LogWindow():
def __init__(self, parent):
self.parent = parent
self.frame = tkinter.Frame(self.parent)
class MainWindow(tkinter.Frame):
def __init__(self, parent):
tkinter.Frame.__init__(self, parent)
self.parent = parent
frame = tkinter.Frame(self, borderwidth=1)
frame.pack(fill=tkinter.BOTH, expand=True, padx=5, pady=5)
self.LogButton = tkinter.Button(frame, text="Log Viewer", command= self.openLogWindow)
self.LogButton.grid(sticky=tkinter.E+tkinter.W)
self.pack(fill=tkinter.BOTH,expand=True)
def openLogWindow(self):
self.logWindow = tkinter.Toplevel(self.parent)
self.app = LogWindow(self.logWindow)
def main():
global app, stopRead
root = tkinter.Tk()
root.geometry("300x300")
app = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
Maybe i need to have a single instance of a Toplevel class and call show and close to show or hide the secondary window.
Finally after some tests I've found how to solve that, thanks to the #furas response and some investigation about the Tkinter events with the protocol function.
I've got that working with:
import tkinter
logWindowExists = False
class LogWindow():
def __init__(self, parent):
global logWindowExists, root
logWindowExists = True
self.parent = parent
self.frame = tkinter.Frame(self.parent)
def on_closing(self):
global logWindowExists
logWindowExists = False
self.parent.destroy()
class MainWindow(tkinter.Frame):
def __init__(self, parent):
tkinter.Frame.__init__(self, parent)
self.parent = parent
frame = tkinter.Frame(self, borderwidth=1)
frame.pack(fill=tkinter.BOTH, expand=True, padx=5, pady=5)
self.LogButton = tkinter.Button(frame, text="Log Viewer", command= self.openLogWindow)
self.LogButton.grid(sticky=tkinter.E+tkinter.W)
self.pack(fill=tkinter.BOTH,expand=True)
def openLogWindow(self):
if not logWindowExists:
self.logWindow = tkinter.Toplevel(self.parent)
self.app = LogWindow(self.logWindow)
self.logWindow.protocol("WM_DELETE_WINDOW", self.app.on_closing)
else:
self.logWindow.deiconify()
def main():
global app, stopRead, root
root = tkinter.Tk()
root.geometry("300x300")
app = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
Using a boolean to know if the window exists or not i can handle when the window it's opened or not and just show the existing window or creating a new one.