Subhranil
Subhranil

Reputation: 21

I get an error: bad window path name ".!frame" when i press a button in the following code?

I am trying to make two different screens and link them with each other using a button on each screen assigned respected functions to them as command.

Please help me to know why I get the following error :

bad window path name ".!frame"

when I press a button in the following code.

from tkinter import *

root = Tk()
my_height = 600
my_width = 350
root.geometry(f"{my_height}x{my_width}")
# root.maxsize(my_height, my_width)

# FRAMES
main_frame = Frame(root, bg="Red", borderwidth=30)
other_frame = Frame(root, bg="Yellow", borderwidth=30)


# first window
def main_screen():
    other_frame.destroy()
    Label(main_frame, text="First Window").pack()
    Button(main_frame, text="GO", command=second_screen).pack()
    main_frame.pack()


def second_screen():
    main_frame.destroy()
    Label(other_frame, text="Second Window").pack()
    Button(other_frame, text="BACK", command=main_screen).pack()
    other_frame.pack()


main_screen()
root.mainloop()

Upvotes: 2

Views: 153

Answers (2)

10 Rep
10 Rep

Reputation: 2270

The problem is you delete the frame other_frame before when you write other_frame.destroy(). This makes it inaccessible by the rest of the program since you destroyed it.

So there would be two solutions: One would be to, as BryanOakley mentioned, to use .pack(). The other would be to use .place().

Code for .pack():

from tkinter import *

root = Tk()
my_height = 600
my_width = 350
root.geometry(f"{my_height}x{my_width}")
# root.maxsize(my_height, my_width)
def main_screen():
    other_frame.pack_forget()
    main_frame.pack()


def second_screen():
    main_frame.pack_forget()
    other_frame.pack()

# FRAMES
main_frame = Frame(root, bg="Red", borderwidth=30)
other_frame = Frame(root, bg="Yellow", borderwidth=30)
Label(other_frame, text="Second Window").pack()
Button(other_frame, text="BACK", command=main_screen).pack()
Label(main_frame, text="First Window").pack()
Button(main_frame, text="GO", command=second_screen).pack()


# first window

main_screen()
root.mainloop()

Code for .place():

from tkinter import *

root = Tk()
my_height = 600
my_width = 350
root.geometry(f"{my_height}x{my_width}")
# root.maxsize(my_height, my_width)


# first window
def main_screen():
    main_frame.place(relx = 0.5, rely = 0.1, anchor = "center")
    other_frame.place(relx = 1.5, rely = 0.1, anchor = "center")
    main_frame.update()
    other_frame.update()


def second_screen():
    main_frame.place(relx = -0.5, rely = 0.1, anchor = "center")
    main_frame.update()
    other_frame.place(relx = 0.5, rely = 0.1, anchor = "center")
    other_frame.update()
# FRAMES
main_frame = Frame(root, bg="Red", borderwidth=30)
other_frame = Frame(root, bg="Yellow", borderwidth=30)
main_frame.place(relx = 0.5, rely = 0.1, anchor = "center")
other_frame.place(relx = 1.5, rely = 0.1, anchor = "center")
Label(main_frame, text="First Window").pack()
Button(main_frame, text="GO", command=second_screen).pack()
Label(other_frame, text="Second Window").pack()
Button(other_frame, text="BACK", command=main_screen).pack()


main_screen()
root.mainloop()

The advantage of using .place() is you can animate the frame back and forth using a for loop.

Here is the code for that:

from tkinter import *

root = Tk()
my_height = 600
my_width = 350
root.geometry(f"{my_height}x{my_width}")
# root.maxsize(my_height, my_width)


# first window
def main_screen():
    global x1, y1, x2, y2
    for i in range(1000):
        x1 += 0.001
        x2 += 0.001
        main_frame.place(relx = x1, rely = y1, anchor = "center")
        other_frame.place(relx = x2, rely = y2, anchor = "center")
        main_frame.update()
        other_frame.update()
        
    

def second_screen():
    global x1, y1, x2, y2
    for i in range(1000):
        x1 -= 0.001
        x2 -= 0.001
        main_frame.place(relx = x1, rely = y1, anchor = "center")
        other_frame.place(relx = x2, rely = y2, anchor = "center")
        main_frame.update()
        other_frame.update()
    
# FRAMES
x1, y1 = 0.5, 0.1
x2, y2 = 1.5, y1
main_frame = Frame(root, bg="Red", borderwidth=30)
other_frame = Frame(root, bg="Yellow", borderwidth=30)
main_frame.place(relx = 0.5, rely = 0.1, anchor = "center")
other_frame.place(relx = 1.5, rely = 0.1, anchor = "center")
Label(main_frame, text="First Window").pack()
Button(main_frame, text="GO", command=second_screen).pack()
Label(other_frame, text="Second Window").pack()
Button(other_frame, text="BACK", command=main_screen).pack()



root.mainloop()

Hope this helps!

Upvotes: 2

Bryan Oakley
Bryan Oakley

Reputation: 385970

The first thing you do in main_screen is to destroy other_frame. When you call second_screen, it tries to use that frame as the parent for new widgets but it has been destroyed. That is why you get the error bad window path name ".!frame" - it is bad because it has been destroyed and can no longer be used.

A simple solution is to simply not call other_frame.destroy(). Instead, you can remove it from view with other_frame.pack_forget().

It's important to know that pack works by placing objects in available space, and in the order that pack was called. If you remove and add widgets once the app has started, that may affect the order in which the widgets appear.

In this specific case, since main_screen and other_screen are the only widgets directly in root it doesn't matter, and doesn't affect the reason why you got the error you got.

Upvotes: 2

Related Questions