Search code examples
pythontkinterframes

Changing Frames in Tkinter


I have been teaching some basic programming to a family friend's daughter. We created a nice basic text adventure game in Python which works exactly as it should. However, she really wants to see it graphically and so I've tried to implement this using my extremely basic knowledge of Tkinter. Each chapter is just copied and pasted from the previous one with the button labels and links, text box contents and image references changed. I have placed each individual screen in to a separate function which is being called by clicking on a button on the previous screen. However, my issue is that, instead of replacing the screen with the new one, they are just going on top of each other. So chapter two's images, buttons and text is just being placed below chapter one's.

I can't figure out a way of killing the previous frames and widgets and replacing them with the new ones. I have tried to kill them several ways but it's either doing nothing, or killing the entire program. I've also tried using the same names for the buttons in each function but because they're local that doesn't really make a difference.

Although I realise it's inefficient, I want to try and stay away from using objects if possible because it will be extremely difficult to explain to her.

Is there any way I can do this by tweaking the structure I have created or can I only do it by using complex classes and objects? Keep in mind that this code is never going to be reused, so it doesn't matter how much of a mess it is in the background.

from tkinter import *


root = Tk()

def intro():


  topFrame = Frame(root)
  topFrame.pack()
  bottomFrame = Frame(root)
  bottomFrame.pack(side = BOTTOM)

  photo = PhotoImage(file="hh.gif")
  label = Label(root, image=photo)
  label.pack(side = TOP)

  t = Text(wrap=WORD, height = 10)
  t.insert(INSERT, "This is the first screen")
  t.insert(END, "")
  t.pack()

  # button 1 here should take us to the second screen
  button1 = Button(bottomFrame, text="Option 1", fg="black", command=chapter2)
  button2 = Button(bottomFrame, text="Option 2", fg="black")
  button3 = Button(bottomFrame, text="Option 3", fg="black")

  button1.pack(side = LEFT)
  button2.pack(side = LEFT)
  button3.pack(side = LEFT)

  root.mainloop()


def chapter2():


  topFrame = Frame(root)
  topFrame.pack()
  bottomFrame = Frame(root)
  bottomFrame.pack(side = BOTTOM)

  photo1 = PhotoImage(file="hh2.gif")
  label1 = Label(root, image=photo1)
  label1.pack(side = TOP)

  t1 = Text(wrap=WORD)
  t1.insert(INSERT, "This is the second screen")
  t1.insert(END, "")
  t1.pack()

  button4 = Button(bottomFrame, text="this", fg="black")
  button5 = Button(bottomFrame, text="that", fg="black")
  button6 = Button(bottomFrame, text="the other", fg="black")

  button4.pack(side = LEFT)
  button5.pack(side = LEFT)
  button6.pack(side = LEFT)

  root.mainloop()

  intro()

Solution

  • What's happening in your code is that you're creating new text fields and labels without removing the old ones.

    To delete the old text/label widgets, use pack_forget(), in your case, t.pack_forget(), label.pack_forget(), button1.pack_forget(), button2.pack_forget(), and button3.pack_forget(). What pack_forget() does is romoving a widget from the window, like the reverse of pack(). So, to add this in your code, simply change line 24:

    button1 = Button(bottomFrame, text="Option 1", fg="black", command=chapter2  
    

    to

    button1 = Button(bottomFrame, text="Option 1", fg="black", command=lambda: (t.pack_forget(), label.pack_forget(), button1.pack_forget(),  button2.pack_forget(), button3.pack_forget(), chapter2()))
    

    (note that in chapter two, you would use t1.pack_forget(), label1.pack_forget(), button4/5/6.pack_forget() and not t/label/button1/2/3.pack_forget())

    You also have to keep a reference to your photo on your label, you can do this by adding label.image = photo between label = Label(topFrame, image=photo) and label.pack(side=TOP) Also, see this page.
    Entier Edited code:

    from tkinter import *
    
    
    root = Tk()
    
    def intro():
    
      topFrame = Frame(root)
      topFrame.pack()
      bottomFrame = Frame(root)
      bottomFrame.pack(side=BOTTOM)
    
      photo = PhotoImage(file="hh.gif")
      label = Label(root, image=photo)
      label.image = photo # keep a reference!
      label.pack(side=TOP)
    
      t = Text(wrap=WORD, height=10)
      t.insert(INSERT, "This is the first screen")
      t.insert(END, "")
      t.pack()
    
      # button 1 here should take us to the second screen
      button1 = Button(bottomFrame, text="Option 1", fg="black", command=lambda: (t.pack_forget(), label.pack_forget(), button1.pack_forget(), button2.pack_forget(), button3.pack_forget(), chapter2()))
      button2 = Button(bottomFrame, text="Option 2", fg="black")
      button3 = Button(bottomFrame, text="Option 3", fg="black")
    
      button1.pack(side=LEFT)
      button2.pack(side=LEFT)
      button3.pack(side=LEFT)
    
    def chapter2():
    
      topFrame = Frame(root)
      topFrame.pack()
      bottomFrame = Frame(root)
      bottomFrame.pack(side=BOTTOM)
    
      photo1 = PhotoImage(file="hh2.gif")
      label1 = Label(root, image=photo1)
      label.image = photo # keep a reference!
      label1.pack(side=TOP)
    
      t1 = Text(wrap=WORD)
      t1.insert(INSERT, "This is the second screen")
      t1.insert(END, "")
      t1.pack()
    
      button4 = Button(bottomFrame, text="this", fg="black")
      button5 = Button(bottomFrame, text="that", fg="black")
      button6 = Button(bottomFrame, text="the other", fg="black")
    
      button4.pack(side=LEFT)
      button5.pack(side=LEFT)
      button6.pack(side=LEFT)
    
    intro()
    
    root.mainloop()
    

    Hope this helps, if it doesn't, please comment and I'll try to find another solution :)

    Also, if you're wondering what lambda is (Part of the edited code), simply google "python lambda".