TXTank
TXTank

Reputation: 25

Python - using grid layout to format a window

I am teaching myself Python and attempting to build a local app that uses a window GUI. I am having a lot of trouble trying to layout the screen with grid(). I have searched and tried lots of different snippets of code, but I have the same problem, the frames and widgets don't seem to be formatting. The code below is really simplistic, but my end goal is to master how to use grid() so I can build any GUI I like in the future.

I want to be able to do the following:

--Window-----------------
| Section 1 | Section 2 |
|           |           |
-------------------------
| Section 3             |
|                       |
|                       |
-------------------------

from Tkinter import Button, Frame, Entry, Tk, Label, Menubutton, Menu, IntVar

class MainScreen(Frame):
def __init__(self, master):
    Frame.__init__(self, master)
    self.grid()
    self.searchSection()
    self.quitButton()


def searchSection(self):
    # Create Search Section
    self.searchFrame = Frame(self.master, bg='grey', relief='sunken', width=200, height=200)
    self.searchFrame.grid(row=0, column=0, rowspan=5, columnspan=30, sticky="wens")
    Label(self.searchFrame, text="Search :", bg='grey').grid(row=1, column=1, columnspan=20, sticky='w')
    self.searchField = Entry(self.searchFrame)
    self.searchField.grid(row=2, column=1, columnspan=7, sticky='w')
    #Create Menu Options
    self.search = Menubutton(self.searchFrame, text = "Search", bg='grey')
    self.search.grid(row=2, column=8, columnspan=3, sticky='w')
    self.search.menu = Menu(self.search, tearoff = 0)
    self.search['menu'] = self.search.menu       
    self.SearchType1Var = IntVar()
    self.search.menu.add_checkbutton(label="SearchType1", variable = self.SearchType1Var)


def quitButton(self):
    ## Provide a quit button to exit the rogram
    self.quitFrame = Frame(self.master, bg='grey', width=50, height=50)
    self.quitFrame.grid(row=0, column=20, rowspan=5, columnspan=5, sticky='ewns')
    self.quitButton = Button(self.quitFrame, text="Quit", command=exit)
    self.quitButton.grid()


if __name__ == '__main__':

root = Tk()
root.title("Learn Grid GUI")
root.geometry("800x600+200+150")
main = MainScreen(root)
root.mainloop()

Upvotes: 1

Views: 5403

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 385890

Your instincts of using subframes is good, but your implementation is making the problem harder than it should be. For a general rule of thumb, I advise against having different functions creating widgets and placing them in their parent. Put another way, all of the grid or pack statements for a given container should normally all be in the same function.

In my experience, the best way to accomplish a layout is to "divide and conquer". For example, your ascii drawing shows three sections. Start with that, and only that. Create three frames, one for each section. Get them placed appropriately in their container before trying to add anything to them.

When I do this, I first give each frame a distinct color and an explicit size just for visualization purposes. For example:

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, master, *args, **kwargs):
        tk.Frame.__init__(self, master, *args, **kwargs)
        self.master = master
        self.s1 = tk.Frame(self, background="pink", width=200, height=200)
        self.s2 = tk.Frame(self, background="blue", width=200, height=200)
        self.s3 = tk.Frame(self, background="bisque", width=200, height=200)

        self.s1.grid(row=0, column=0, sticky="nsew")
        self.s2.grid(row=0, column=1, sticky="nsew")
        self.s3.grid(row=1, column=0, columnspan=2, sticky="nsew")

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

Once I have that working and resizing properly, I can then move on to adding widgets, one section at a time. Since I've already got the main parts of the GUI responding appropriately to window resizes, I can focus all my efforts on a single section without worrying about how they affect the rest of the program.

It's important to know that when you add widgets to one of these sections, the rows and columns within that section are independent of any other widgets. For example, if you put something in column one of section 2, that refers to column one inside section two, not the absolute column of the GUI as a whole.

For illustrative purposes, let's assume you want the search label, entry widget, and the menu button all to go in section 3 (I can't tell from your question where you want them to go). In this case, the label goes on row 0 and spans two columns, and the entry widget and menubutton go below, in separate columns. I assume you want the first column to grow and shrink.

This requires two modifications to the program. The first is to create the searchSection function, which creates the widgets in the search section. The second modification is to call this function in the constructor of the GUI.

Here's a fully working final version:

import Tkinter as tk

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

        self.s1 = tk.Frame(self, background="pink", width=200, height=200)
        self.s2 = tk.Frame(self, background="blue", width=200, height=200)
        self.s3 = tk.Frame(self, background="bisque", width=200, height=200)

        self.s1.grid(row=0, column=0, sticky="nsew")
        self.s2.grid(row=0, column=1, sticky="nsew")
        self.s3.grid(row=1, column=0, columnspan=2, sticky="nsew")

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)

        # now create the search section
        self.searchSection(self.s1)

    def searchSection(self, parent):
        l = tk.Label(parent, text="Search :", bg='grey')
        self.searchField = tk.Entry(parent)
        self.search = tk.Menubutton(parent, text="Search", bg="grey")
        self.search.menu = tk.Menu(self.search, tearoff = 0)
        self.search['menu'] = self.search.menu
        self.SearchType1Var = tk.IntVar()
        self.search.menu.add_checkbutton(label="SearchType1", 
                                         variable = self.SearchType1Var)

        l.grid(row=0, column=0, columnspan=2, sticky='w')
        self.searchField.grid(row=1, column=0, sticky="ew")
        self.search.grid(row=1, column=1, sticky="ew")

        parent.grid_columnconfigure(0, weight=1)

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

Upvotes: 2

LStyle
LStyle

Reputation: 1

If I understood correctly, your problem is that you cant sort the GUI. The answer is pretty simple - if you want to sort the GUI just put the code in the same order that you want the window to show. For example:

 --Window--
| text box |
|  button  |
|          |
------------

-Code-
[text box code here]
[button code here]
root.mainloop()

Ive been working with tkinter for a long time and it worked for me. Good luck and happy holydays!

LStyle

Upvotes: 0

Related Questions