How to place widgets into different frames in Tkinter, using OOP

13,552

Solution 1

I finally found the answer. From what I have found (feel free to edit this if it is wrong) is that there are only two ways that a Frame can be inherited in Tkinter: from the class itself and from the method that a widget is currently in. To solve the issue, I set up the class Application as a frame, and then place other frames within it. Here is a basic rendition of what I did:

#Import Tkinter
from Tkinter import *

#Main Frame
class Application(Frame):
    def __init__(self, master):  #initialize the grid and widgets
        Frame.__init__(self,master)
        self.grid()
        self.redFUN() #initialize the red frame's Function
        self.greenFUN() #initialize the green frame's Function
        self.widgets() #To show that you can still place non-Frame widgets 
    def widgets(self):
        self.mylabel = Label (self, text = "Hello World!")
        self.mylabel.grid()
    def redFUN(self): #The 'self' means that it is an instance of the main frame
        #Init the red frame
        self.redFrame = Frame(root, width = 100, height = 50,pady = 5,
                              bg = "red")
        self.redFrame.grid()



    def greenFUN(self): #Child of the mainframe
        self.greenFrame = Frame(root, width = 100, height = 50,pady = 5,
                          bg = "green") #it is green!
        self.greenFrame.grid()








#These lines of code are used for the grid
root = Tk()
root.title("Frame Example")
root.geometry("300x300")
app = Application(root)

root.mainloop()

I hope this helps everyone - if you have any questions, feel free to comment!

Solution 2

Problem 1:

A widget can only have one immediate parent. There's no syntax for passing in two parents. You seem to be passing in both self and self.toolbar as parents of self.saveButton, for example.

myButton = Button(self.toolbar, text="Blah", command=self.someCommand)

is the form you should use.

Problem 2:

Let's say that you instead wanted Application (AKA the self in Button(self, self.toolbar...)) to be the parent of myButton. This won't work either, because in order to be the hierarchical parent of a Tk widget, a class must also be an instance of Widget. Usually, what you do if you want this is to inherit tk.Tk() in Application as follows:

class Application(Tk):

    def __init__(self, *args, **kwargs):


        Tk.__init__(self, *args, **kwargs) #It's important that you call the parent class's __init__ method first

        self.createWidgets()

    def createWidgets(self):

        self.myButton = Button(self, text="Blah", command=lambda x: print "x")
        #this is ok, because now self (AKA Application) is a valid instance of Tk
Share:
13,552
xxmbabanexx
Author by

xxmbabanexx

I am just starting out with programming, and the StackOverflow website is epic! I head the Python Programming Club at my middle school and am learning alongside my friends. My languages: Python (Including Tkinter) HTML (A little bit) I have the Tumbleweed badge due to this lovely post on Super User (Which is actually relevant.) My 'greatest' answer ever!

Updated on June 04, 2022

Comments

  • xxmbabanexx
    xxmbabanexx almost 2 years

    Background

    Hello everyone! I am currently working on a basic GUI text editor which can load and save text files. I want to use multiple frames for the toolbar and the textbox as I learned here. I am using OOP, and have set up my frames in the __init__ method, and the widgets in the widget method. For some reason, the widgets are unable to be placed within their respective Frames.

    Code

    from Tkinter import *
    class Application:
        def __init__(self,parent):  #initialize the grid and widgets
            self.myParent = parent
    
            #Init the toolbar
            self.toolbar = Frame(parent)
            self.toolbar.grid(row = 0)
    
            #Init frame for the text box
    
            self.mainframe = Frame(parent)
            self.toolbar.grid(row = 1)
        def widget(self):#Place widgets here
    
            #Save Button
            self.saveButton = Button (self, self.toolbar,
                                      text = "Save", command = self.saveMe)
            self.saveButton.grid(column = 0, row = 0, sticky = W)
    
            #Open Button
            self.openButton = Button (self, self.toolbar,
                                     text = "Open", command = self.openMe)
            self.openButton.grid(column = 0, row = 1, sticky = W)
            #Area where you write 
            self.text = Text (self, self.mainframe,
                              width = (root.winfo_screenwidth() - 20),
                              height = (root.winfo_screenheight() - 10))
           self.text.grid(row = 2)
    

    Questions

    1. Still using different methods, how can I make sure that each widget is placed in the correct Frame?

      • If this is not possible, please just show me how to do it using OOP - I am most comfortable with Tkinter in that setting and have promised myself to improve.
    2. Please explain your answer. I need to cognate - not simply nod my head at the computer and go right along.

    3. Extra Credit: How would I initialize multiple windows (each window being a different class) using Tkinter in OOP? For instance, if this was my code:

      class MainWindow(Frame):
          ---init stuff---
          def widget(self):
              newWindow = Button(self, text = "click for a new window",
                                 command = self.window)
              newWindow.grid()
         def window(self):
               #What would I put in here to initialize the new window??
      
      class theNextWindow(Frame):
      

      What would I put in the window.self method to make the theNextWindow window visible?

    Thanks for everyone's help!

    EDIT 1

    I added the line self.widget() in the __init__ method, and I was rewarded with this "wonderful" error:

    Traceback (most recent call last):
    File "D:\Python Programs\Text Editor\MyTextv2.py", line 67, in <module>
     app = Application(root)
    File "D:\Python Programs\Text Editor\MyTextv2.py", line 14, in __init__
     self.widget()
    File "D:\Python Programs\Text Editor\MyTextv2.py", line 24, in widget
     text = "Save", command = self.saveMe)
    File "C:\Python27\lib\lib-tk\Tkinter.py", line 2044, in __init__
     Widget.__init__(self, master, 'button', cnf, kw)
    File "C:\Python27\lib\lib-tk\Tkinter.py", line 1965, in __init__
     BaseWidget._setup(self, master, cnf)
    File "C:\Python27\lib\lib-tk\Tkinter.py", line 1943, in _setup
     self.tk = master.tk
    AttributeError: Application instance has no attribute 'tk'
    

    As the error log clearly references my mainloop here: File "D:\Python Programs\Text Editor\MyTextv2.py", line 67, in <module> app = Application(root) I decided to add it:

    root = Tk()
    root.title("My Text Editor")
    
    #This is wierd - it gets the computer windows dimensions
    w, h = root.winfo_screenwidth(), root.winfo_screenheight()
    root.overrideredirect(0)
    
    #And then applies them here
    root.geometry("%dx%d+0+0" % (w, h))
    
    app = Application(root)
    
    root.mainloop()