BeginnerSQL74651
BeginnerSQL74651

Reputation: 35

Tkinter Grid Problem - Frames not aligned

I just started to work with tkinter and I'm still trying to get used to how it works. I was trying to prepare some sort of grid menu with 4 sections.

There is one frame at the top with the title of the app, one frame on the left with some buttons to configure the app and then, 4 equally sized frames in the rest.

My main issue here is the blank column between the left frame and the other four, how can I adjust this so the 4 squares fill in that space?

See below the code:

import datetime
import tkinter as tk
from time import strftime

window = tk.Tk()
window.geometry("1400x800")
window.configure(bg="white")
window.rowconfigure(20,weight=1)
window.columnconfigure(35,weight=1)
window.title("Hello World App")
entry_var_server = tk.StringVar(window,"")
entry_var_db = tk.StringVar(window,"")
entry_var_driver = tk.StringVar(window,"")


def window_widgets():
    db_ini_frame_top = tk.Frame(master=window,bg="#57b956",height=120,width=1400,highlightbackground="black",highlightthickness=2)
    db_ini_frame_top.grid(rowspan=3,columnspan=34,sticky="w")

    db_ini_label_top = tk.Label(master=window,text="Hello World",bg="#57b956")
    db_ini_label_top.configure(font=("Calibri",26))
    db_ini_label_top.grid(row=1,column=18,sticky="n")

    def cur_date(dic = {'01':'st','21':'st','31':'st',
                    '02':'nd','22':'nd',
                    '03':'rd','23':'rd'}):
        x = strftime('%A,  %B %d')
        return x + dic.get(x[-2:],'th')+strftime(" %G - %H:%M")

    date = cur_date()
    db_ini_date = tk.Label(master=window,text=date,bg="#57b956")
    db_ini_date.configure(font=("Calibri",12))
    db_ini_date.grid(row=0,column=0,sticky="w")

    db_ini_frame_left = tk.Frame(master=window,bg="light grey",height=800,width=120,colormap="new",highlightbackground="black",highlightthickness=2)
    db_ini_frame_left.grid(row=3,rowspan=16,columnspan=2,sticky="w")

    db_ini_frame_center_nw = tk.Frame(master=window,height=350,width=640,bg="blue")
    db_ini_frame_center_nw.grid(row=3,column=3,columnspan=16,rowspan=12,sticky="nw")

    db_ini_frame_center_sw = tk.Frame(master=window,height=450,width=640,bg="light blue")
    db_ini_frame_center_sw.grid(row=12,column=3,columnspan=16,rowspan=12,sticky="sw")

    db_ini_frame_center_ne = tk.Frame(master=window,height=350,width=640,bg="light blue")
    db_ini_frame_center_ne.grid(row=3,column=19,columnspan=16,rowspan=12,sticky="ne")

    db_ini_frame_center_se = tk.Frame(master=window,height=450,width=640,bg="blue")
    db_ini_frame_center_se.grid(row=12,column=19,columnspan=16,rowspan=12,sticky="se")

window_widgets()
window.tk.mainloop()

Upvotes: 1

Views: 2340

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 385980

While we can fix your code with just a couple of small changes, taking a few minutes to create a smarter layout will make it much easier to modify your code as the application grows.

I'm going to offer some advice about how I would reorganize your code. At the end of the answer is a final working example. 90% of it is your original code, but with a few tweaks here and there to make creating the UI easier.

Don't use grid for everything

First, I recommend that you not use grid for everything. It's good for creating grids, but there are sometimes better ways to lay out an entire window. Divide your UI into logical groupings, and then lay them out using those groupings.

By organizing your UI into sections it makes it much easier to modify just a single section. When you use grid for everything, making one small change might require you to change a dozen lines of code where you have to adjust row and column numbers, and row and column spans for everything just to accommodate a single new widget.

Group layout code together

I also recommend that you group your calls to grid and pack together rather than sprinkling them throughout the code. I find it makes it much easier to visualize the layout, and easier to make changes since all related changes are close to each other rather than spread out.

Divide the UI into logical sections

You clearly have three distinct areas: a title across the top, some buttons on the left, and then everything else. So, start by creating just that. pack is arguably the best choice when you have things arranged along the sides of a space.

So, start out by creating frames for those three areas. You can then use pack to align them along the edges per your description.

db_ini_frame_top = tk.Frame(master=window, ...)
db_ini_frame_left = tk.Frame(master=window, ...)
db_ini_main = tk.Frame(master=window, ...)

db_ini_frame_top.pack(side="top", fill="x")
db_ini_frame_left.pack(side="left", fill="y")
db_ini_main.pack(side="top", fill="both", expand=True)

Place other widgets in these containers

Even though you created a frame at the top, you were putting the label and date in the root window. You should be placing them inside the frame. When you get around to adding buttons, they should go in the left frame. And finally, for the other four windows, they need to go in the third frame.

db_ini_label_top = tk.Label(master=db_ini_frame_top, ...)
db_ini_date = tk.Label(master=db_ini_frame_top, ...)

db_ini_frame_center_nw = tk.Frame(master=db_ini_main, ...)
db_ini_frame_center_sw = tk.Frame(master=db_ini_main, ...)
db_ini_frame_center_ne = tk.Frame(master=db_ini_main, ...)
db_ini_frame_center_se = tk.Frame(master=db_ini_main, ...)

Use grid for the four frames that are actually in a grid

When using grid, a rule of thumb is that you should give at least one row and one column a weight so that grid knows what to do with extra space as the window grows or shrinks. In your case you want all four cells to be equal, so you should give them all an equal weight.

Because you now are using grid only for these four windows instead of trying to force everything into a single grid, you can use row and column numbers that make sense (ie: rows 0 and 1, columns 0 and 1, instead of artificially large rows and columns and spans).

db_ini_frame_center_nw = tk.Frame(master=db_ini_main,height=350,width=640,bg="blue")
db_ini_frame_center_sw = tk.Frame(master=db_ini_main,height=450,width=640,bg="light blue")
db_ini_frame_center_ne = tk.Frame(master=db_ini_main,height=350,width=640,bg="light blue")
db_ini_frame_center_se = tk.Frame(master=db_ini_main,height=450,width=640,bg="blue")

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

db_ini_frame_center_nw.grid(row=0,column=0, sticky="nsew")
db_ini_frame_center_sw.grid(row=0,column=1, sticky="nsew")
db_ini_frame_center_ne.grid(row=1,column=0, sticky="nsew")
db_ini_frame_center_se.grid(row=1,column=1, sticky="nsew")

This is what your full code looks like. I didn't adjust the location of the text in the title area, but it's easy to change it without affecting the other areas of the GUI.

Also, notice that you can manually resize the window and everything grows or shrinks appropriately rather than leaving empty spots.

import datetime
import tkinter as tk
from time import strftime

window = tk.Tk()
window.geometry("1400x800")
window.configure(bg="white")
window.rowconfigure(20,weight=1)
window.columnconfigure(35,weight=1)
window.title("Hello World App")
entry_var_server = tk.StringVar(window,"")
entry_var_db = tk.StringVar(window,"")
entry_var_driver = tk.StringVar(window,"")


def window_widgets():
    db_ini_frame_top = tk.Frame(master=window,bg="#57b956",height=120,width=1400,highlightbackground="black",highlightthickness=2)
    db_ini_frame_left = tk.Frame(master=window,bg="light grey",height=800,width=120,colormap="new",highlightbackground="black",highlightthickness=2)
    db_ini_main = tk.Frame(master=window,bg="light grey",height=800,width=120,colormap="new",highlightbackground="black",highlightthickness=2)

    db_ini_frame_top.pack(side="top", fill="x")
    db_ini_frame_left.pack(side="left", fill="y")
    db_ini_main.pack(side="top", fill="both", expand=True)

    db_ini_label_top = tk.Label(master=db_ini_frame_top,text="Hello World",bg="#57b956")
    db_ini_label_top.configure(font=("Calibri",26))
    db_ini_label_top.grid(row=1,column=18,sticky="n")

    def cur_date(dic = {'01':'st','21':'st','31':'st',
                    '02':'nd','22':'nd',
                    '03':'rd','23':'rd'}):
        x = strftime('%A,  %B %d')
        return x + dic.get(x[-2:],'th')+strftime(" %G - %H:%M")

    date = cur_date()
    db_ini_date = tk.Label(master=db_ini_frame_top,text=date,bg="#57b956")
    db_ini_date.configure(font=("Calibri",12))
    db_ini_date.grid(row=0,column=0,sticky="w")

    db_ini_frame_center_nw = tk.Frame(master=db_ini_main,height=350,width=640,bg="blue")
    db_ini_frame_center_sw = tk.Frame(master=db_ini_main,height=450,width=640,bg="light blue")
    db_ini_frame_center_ne = tk.Frame(master=db_ini_main,height=350,width=640,bg="light blue")
    db_ini_frame_center_se = tk.Frame(master=db_ini_main,height=450,width=640,bg="blue")

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

    db_ini_frame_center_nw.grid(row=0,column=0, sticky="nsew")
    db_ini_frame_center_sw.grid(row=0,column=1, sticky="nsew")
    db_ini_frame_center_ne.grid(row=1,column=0, sticky="nsew")
    db_ini_frame_center_se.grid(row=1,column=1, sticky="nsew")


window_widgets()
window.tk.mainloop()

screenshot

Upvotes: 2

Mike67
Mike67

Reputation: 11342

The date field is in a single column so it is pushing the the next column to the right.

Update your date block and add columnspan=4

date = cur_date()
db_ini_date = tk.Label(master=window,text=date,bg="#57b956")
db_ini_date.configure(font=("Calibri",12))
db_ini_date.grid(row=0,column=0,columnspan=4,sticky="w")   # Allow 4 columns for date 

Output

enter image description here

Upvotes: 0

Related Questions