I am using python 2.6 and am new to using Tkinter. I am trying to create a basic template GUI for a Toplevel Frame with multiple frames inside of it. I ran into an issue where I am not able to clear my frames properly. I think I have an inheritance wrong inside of my frame classes but I am not able to fix it without some help. I have not added anything to the commands beside updating the GUI in order to figure out my problem. Thanks.

from Tkinter import *

class F1(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.test(master)

    def test(self, master):
        self.bttn = Button(master.top)
        self.bttn["text"] = "Change Frame"
        self.bttn["command"] = master.getF2
        self.bttn.grid()

class F2(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.test(master)

    def test(self, master):
        self.lbl = Label(master.top, text = "You did It!")
        self.lbl.grid()

        self.bttn = Button(master.top)
        self.bttn["text"] = "Change Frame Again?"
        self.bttn["command"] = master.getF3
        self.bttn.grid()

class F3(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.test(master)

    def test(self, master):
        self.lbl = Label(master.top, text = "You did It Again!")
        self.lbl.grid()

class App(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.grid()
        
        self.vartext = StringVar()
        self.current_frame = ""
        
        self.create_window()

    def create_window(self):
        """Main GUI"""
        self.bttn1 = Button(self)
        self.bttn1["text"] = "Add Food"
        self.bttn1["command"] = self.top_window
        self.bttn1.grid()

    def top_window(self):
        self.top = Toplevel()
        self.top.title("Add Food to Log")
        self.head_container = Frame(self.top)
        self.head_container.grid(row = 1)
        self.backbttn = Button(self.head_container, text = "Back Frame", command = self.changeFrame).grid(row = 1, column = 0)
        self.head_lbl = Label(self.head_container, textvariable = self.vartext).grid(row = 1, column = 1)
        self.exitbttn = Button(self.head_container, text = "Exit", command = self.top.destroy).grid(row = 1, column = 2)

        self.container = Frame(self.top)
        self.lbl = Label(self.container, text = "Test").grid()
        self.container.grid(row = 2)
        self.getF1()
        
    def changeFrame(self):
        if self.current_frame == "F2":
            self.getF1()
        if self.current_frame == "F3":
            self.getF2()
        

    def getF1(self):
        self.container.grid_forget()
        self.current_frame = "F1"
        print self.current_frame
        self.container = F1(self)
        self.vartext.set("Frame 1")
        self.container.grid()

    def getF2(self):
        self.current_frame = "F2"
        print self.current_frame
        self.container.grid_forget()
        self.container = F2(self)
        self.vartext.set("Frame 2")
        self.container.grid()

    def getF3(self):
        self.current_frame = "F3"
        print self.current_frame
        self.container.grid_forget()
        self.container = F3(self)
        self.vartext.set("Frame 3")
        self.container.grid()
        
# Main Code
root = Tk()
root.title("GUI Test")

app = App(root)

root.mainloop()

When the program executes the getF1() function at the end of the top_window function, it is suppose to clear the second frame created in the toplevel window. However the button stays when the it is clicked. It is suppose to be cleared using grid_forget() and then the second frame class is suppose to load in the place of frame one. I think that I am not getting the F1 class to be called to the correct frame class inside of the toplevel window that is suppose to contain to frames. Can someone help me figure out where my error is?

Can you boil that down to just the minimum code required to do what you want to do?

I have started by stripping down the code to the point that I start having trouble. If you run this code:

from Tkinter import *

class App(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.grid()
        
        self.create_window()

    def create_window(self):
        """This will be the main window of my GUI"""
        self.bttn1 = Button(self)
        self.bttn1["text"] = "Click Me First"
        self.bttn1["command"] = self.top_window
        self.bttn1.grid()
    
    def top_window(self):
        """This is a custom dialog GUI"""
        print "Top Frame"
        self.top = Toplevel()
        self.top.title("Top Window")

        self.top_frame = Frame(self.top)
        self.top_frame.grid()
        
        self.top_lbl = Label(self.top_frame)
        self.top_lbl["text"] = "Top Window Frame Label"
        self.top_lbl.grid()

        self.bottom_frame = Frame(self.top)
        self.bottom_frame.grid()
        self.bttn2 = Button(self.bottom_frame)
        self.bttn2["text"] = "Change This Frame"
        self.bttn2["command"] = self.update_frame   # This is the button that is going to update the bottom_frame
        self.bttn2.grid()
        
# Main Code
root = Tk()
root.title("GUI Test")

app = App(root)

root.mainloop()

It will bring up a main window with a button. When that button is pressed, it launches a toplevel window that is where I want the user to input data that will be passed back to the first window. I would like to click the button on the toplevel frame and clear the bottom frame in the toplevel with a new frame with a different button configuration. I would like to use a class structure for all the frames that are loaded into the bottom frame of the toplevel window. Vegaseat, I really appreciate the help. Tell me what I need to do to get your help on solving this. Thank you

cnuzzo,

I don't want to rework your code to make it work. Instead, I will give you an example that I think you can apply to your situation in a similar way. I will only create one top level window however.

It works like this:

  1. Create the top level frame

  2. Establish criteria for the frames you intend to switch

  3. Create your frames using established criteria, save the frame objects in a dictionary using keys that can be recalled later, and apply

grid() or pack()
Create an instance of FrameManager (code shown below) and for this example, choose an initial frame
Bind a command to a button or buttons that chooses the current frame

import Tkinter
from itertools import cycle

class FrameManager(object):
    def __init__(self, frameDict, firstIdx):
        self.frameDict = frameDict
        for key in frameDict:
            frameDict[key].grid_forget()
        self.showing = self.frameDict[firstIdx]
        self.showing.grid()

    def show_frame(self, idx):
        self.showing.grid_forget()
        self.showing = self.frameDict[idx]
        self.showing.grid()

class MakeFrame(LabelFrame):
    def __init__(self, master, btnLabel):
        Tkinter.LabelFrame.__init__(self, master, text=btnLabel)
        self.btn = Tkinter.Button(self, text=btnLabel,
                                  command=master.nextBtn)
        self.label = Tkinter.Label(self, text="%s is current" % (btnLabel))
        self.grid()
        self.btn.grid()
        self.label.grid()

class App(Frame):
    def __init__(self, master):
        Tkinter.Frame.__init__(self, master)
        self.grid()
        nameList = ["Frame 1", "Frame 2", "Frame 3"]
        self.frameiter = cycle(nameList)
        frameDict = {}
        for frameName in nameList:
            frameDict[frameName] = MakeFrame(self, frameName)
        self.fm = FrameManager(frameDict, self.frameiter.next())

    def nextBtn(self):
        self.fm.show_frame(self.frameiter.next())

The above merely cycles through the established frames, but the principal could be applied to your situation.

Here's another example that is a bit more complicated. I will post just part of the code. The object is to enter a quantity in one unit and convert to a different unit. I only want to display one conversion at a time (example inches to millimeters). The various supported conversions are displayed in a menu named convertMenu.

class ConversionApp(Tkinter.Tk):
    def __init__(self, master=None):
        Tkinter.Tk.__init__(self, master)
        self.convertList = ["In->MM", "MM->In",
                            "Feet->Meters", "Meters->Feet",
                            "Lb->Kg", "Kg->Lb",
                            "Acres->Sq Ft", "Sq Ft->Acres"]
        self.convertDict = dict(zip(self.convertList,
                                  [("in", "mm", 25.4),
                                   ("mm", "in", 1/25.4),
        # snip
        self.frameDict = {}
        for idx, conv in enumerate(self.convertList):
            def handler(idx=idx):
                self.show_convert(idx)
            convertMenu.add_command(label=conv, command=handler)
            self.frameDict[idx] = ConversionFrame(self, *self.convertDict[conv])

        self.fm = FrameManager(self.frameDict, 0)

So with the MakeFrame class in the first example, what do I do if I have frames with different widget configurations? Do I make several classes and then add each instance of those classes to my dictionary before calling any frames?

So with the MakeFrame class in the first example, what do I do if I have frames with different widget configurations? Do I make several classes and then add each instance of those classes to my dictionary before calling any frames?

You don't have to do it that way. Instead of a loop, you can create the frames linearly and add them to the frame dictionary upon creation.

Thank you for you help.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.