srikarad
srikarad

Reputation: 65

Combine multiple frames with widgets together in a single window with tkinter

I am trying to build a GUI which basically has a single window. This window is a combination of different frames. What I want to do is to arrange these frames (Each of the frames further comprise of multiple widgets) together in a certain format:

Below, I have added a simple minimalistic code for the problem. To keep it simple, I have used the same FRAME class and created 4 instances of this class to be added in the main window. I use Tkinter's grid manager to organise the widgets in the frame and then again use grid manager to pack these frames into the main window.

import Tkinter as tk 

# Test frame class. The same frame is used to create 4 instances. 
class TestFrame(tk.Frame): 
    def __init__(self, master=None, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        super().__init__(master)
        
        # Test Entry fields
        test_label_1                = tk.Label(master, text="Test Label 1").grid(row=0, column=0)
        test_label_1_entry          = tk.Entry(master, bd=5).grid(row=0, column=1)
        test_label_2                = tk.Label(master, text="Test Label 2").grid(row=0, column=2)
        test_label_2_entry          = tk.Entry(master, bd=5).grid(row=0, column=3)
    
        # Test checkbox fields
        test_checkbox_1_label       = tk.Label(master, text="Test check button 1").grid(row=2, column=0)
        test_checkbox_1_entry       = tk.Checkbutton(master, bd=5).grid(row=2, column=1)
        test_checkbox_2_label       = tk.Label(master, text="Test check button 2").grid(row=2, column=2)
        test_checkbox_2_entry       = tk.Checkbutton(master, bd=5).grid(row=2, column=3)
        
        # Test dropdowns
        test_dropdown_label         = tk.Label(master, text="Test dropdown 1").grid(row=3, column=0)
        list_of_options             = ["first option", "second option", "third option", "forth option"]
        first_option                = tk.StringVar(master)
        first_option.set(list_of_options[0])
        test_dropdown_label         = tk.OptionMenu(master, first_option, *list_of_options).grid(row=3, column=1)
        test_dropdown_label         = tk.Label(master, text="Test dropdown 1").grid(row=3, column=0)
        list_of_options             = ["first option", "second option", "third option", "forth option"]
        first_option                = tk.StringVar(master)
        first_option.set(list_of_options[0])
        test_dropdown_label         = tk.OptionMenu(master, first_option, *list_of_options).grid(row=3, column=1)
        pass 
    
    pass

class TestMainApplication(tk.Frame):

    def __init__(self, master, *args, **kwargs):
        tk.Frame.__init__(self, master, *args, **kwargs)
        super().__init__(master)

        title                       = "Test Application"
        self.grid()

        # Assign the weight to each row and column as 1. This is required to align the parameters in grid.
        for r in range(3):
            self.master.rowconfigure(r, weight=1)    
        for c in range(8):
            self.master.columnconfigure(c, weight=1)
        
        self.master.title(title)
        
        # Add two instances of the class TestFrame
        self.testFrame1            = TestFrame(master)
        self.testFrame2            = TestFrame(master)
        self.testFrame3            = TestFrame(master)
        self.testFrame4            = TestFrame(master)

        self.testFrame1.grid(row=0, column=0, rowspan=3, columnspan=3, sticky=tk.W+tk.E+tk.N+tk.S)
        self.testFrame2.grid(row=3, column=0, rowspan=3, columnspan=3, sticky=tk.W+tk.E+tk.N+tk.S)  
        self.testFrame3.grid(row=0, column=3, rowspan=2, columnspan=3, sticky=tk.W+tk.E+tk.N+tk.S)
        self.testFrame4.grid(row=2, column=3, rowspan=4, columnspan=3, sticky=tk.W+tk.E+tk.N+tk.S)
              
        pass 
    pass 


# Main application is created here. 
root                   = tk.Tk()
mainApplication        = TestMainApplication(root)
root.mainloop()

I would expect after running this code, a window split into 4 parts organised in the format as discussed above. However, what I see is all the frames overlapped on top of each other.

Enviroment: MacOS, tkinter 8.6, python 3.6.7

Upvotes: 2

Views: 6395

Answers (1)

Mike - SMT
Mike - SMT

Reputation: 15226

Ok so a few problems here.

  1. Your code is somewhat messy. Please consider using the PEP8 standard. Link here

  2. If you are going to use class inherency for your frames why not do the same for your root window. I have edited your code to inherit from Tk().

  3. The reason for overlap is because in your TestFrame class you are telling your widgets they belong to master instead of self (IE the TestFrame class itself). To correct this simply replace all master calls in your TestFrame to self.

  4. You use both tk.Frame.__init__(self) and super().__init__(master). You really only need to use super().__init__() when using this kind of inherency.

See example code below:

import tkinter as tk


class TestFrame(tk.Frame):
    def __init__(self):
        super().__init__()
        list_of_options = ["first option", "second option", "third option", "forth option"]
        first_option = tk.StringVar(self)
        first_option.set(list_of_options[0])

        tk.Label(self, text="Test Label 1").grid(row=0, column=0)
        tk.Entry(self, bd=5).grid(row=0, column=1)
        tk.Label(self, text="Test Label 2").grid(row=0, column=2)
        tk.Entry(self, bd=5).grid(row=0, column=3)
        tk.Label(self, text="Test check button 1").grid(row=2, column=0)
        tk.Checkbutton(self, bd=5).grid(row=2, column=1)
        tk.Label(self, text="Test check button 2").grid(row=2, column=2)
        tk.Checkbutton(self, bd=5).grid(row=2, column=3)
        tk.Label(self, text="Test drop down 1").grid(row=3, column=0)
        tk.OptionMenu(self, first_option, *list_of_options).grid(row=3, column=1)


class TestMainApplication(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Test Application")

        for r in range(3):
            self.rowconfigure(r, weight=1)
        for c in range(8):
            self.columnconfigure(c, weight=1)

        self.testFrame1 = TestFrame()
        self.testFrame2 = TestFrame()
        self.testFrame3 = TestFrame()
        self.testFrame4 = TestFrame()
        self.testFrame1.grid(row=0, column=0, rowspan=3, columnspan=3, sticky='nsew')
        self.testFrame2.grid(row=3, column=0, rowspan=3, columnspan=3, sticky='nsew')
        self.testFrame3.grid(row=0, column=3, rowspan=2, columnspan=3, sticky='nsew')
        self.testFrame4.grid(row=2, column=3, rowspan=4, columnspan=3, sticky='nsew')


TestMainApplication().mainloop()

Upvotes: 6

Related Questions