Skip to content Skip to sidebar Skip to footer

Freezing The Header In A Grid With A Scrollbar In Tkinter

So I finally decided to build a GUI, but I'm stuck bad, and I can't find anything on the internet. What I'm trying to acomplish is basically the 'freeze panes' option from Excel. I

Solution 1:

This is a GUI I'm working on for my tasks list with priority and description that works pretty well. The entry boxes and headers are on separate canvases so that the header will not scroll and the entry boxes will. I had trouble aligning the headers with the entries. The short term fix was to use different fonts that happen to align. I still don't know why explicitly setting widths did not work.

import Tkinter as Tk
from Tkinter import StringVar
#-----------------------------------------------------------------------------
# Scroll entry boxes with mouse wheel
#     canvas1, frame1
#
# Header row is on a separate canvas and will not scroll with entry boxes.
#     canvas2, frame2
#
# Could not align header and entries unless different fonts were used.
# Play with variable "font_choice" to see how it works
#-----------------------------------------------------------------------------
# define fonts and other preliminary things

font_choice = 1
if(font_choice == 1): # using same font sizes does not align well
    Label_Font = "Arial 10"
    Entry_Font = "Arial 10"
if(font_choice == 2): # using different font sizes not exactly aligned, but close
    Label_Font = "Arial 9 bold" 
    Entry_Font = "Arial 10"
if(font_choice == 3): # this does not align well
    Label_Font = "Helvetica 10"
    Entry_Font = "Helvetica 10"
if(font_choice == 4): # this aligns pretty well (surprisingly)
    Label_Font = "Helvetica 14"
    Entry_Font = "Helvetica 14"

width_priority = 10
width_time_estimate = 15
width_description_1 = 90

Labels = ["Priority", "Time Est. (mins)", "Description 1"]

Widths = [width_priority, width_time_estimate, width_description_1]

Label_Colors = ["misty rose", "lavender", "lightcoral"]

list_length = 25

canvas_width = 1300
header_height = 25
#------------------------------------------------------------------------------
def populate1(framex):
    global entrys_1
    global entrys_2
    global entrys_3

    label_flag = 0 # 1 or 2 are options, 0 is off

    ijk_col = 0
    if(label_flag == 1):
        Tk.Label(framex, text=Labels[ijk_col]).grid(row=0, column=ijk_col)
    if(label_flag== 2):
        xy1 = Tk.Label(framex, text=Labels[ijk_col], font=Label_Font)
        xy1.grid(row=0, column=ijk_col)
        xy1.config(width=Widths[ijk_col], borderwidth="1", background=Label_Colors[ijk_col])
    entrys_1 = []
    variables_1 = []
    ii = 0
    for row in range(list_length):
        rowx = row + 1
        variables_1.append(StringVar())
        entrys_1.append(Tk.Entry(framex, textvariable=variables_1[ii], font=Entry_Font))
        entrys_1[-1].config(width=Widths[ijk_col], borderwidth="1")
        entrys_1[-1].grid(row=rowx, column=ijk_col)
        entrys_1[-1].config(fg="black", bg=Label_Colors[ijk_col])
        entrys_1[-1].delete(0, Tk.END)
        entrys_1[-1].insert(0, "Placeholder")
        ii += 1

    ijk_col = 1
    if(label_flag == 1):
        Tk.Label(framex, text=Labels[ijk_col]).grid(row=0, column=ijk_col)
    if(label_flag== 2):
        xy2 = Tk.Label(framex, text=Labels[ijk_col], font=Label_Font)
        xy2.grid(row=0, column=ijk_col)
        xy2.config(width=Widths[ijk_col], borderwidth="1", background=Label_Colors[ijk_col])
    entrys_2 = []
    variables_2 = []
    ii = 0
    for row in range(list_length):
        rowx = row + 1
        variables_2.append(StringVar())
        entrys_2.append(Tk.Entry(framex, textvariable=variables_2[ii], font=Entry_Font))
        entrys_2[-1].config(width=Widths[ijk_col], borderwidth="1")
        entrys_2[-1].grid(row=rowx, column=ijk_col)
        entrys_2[-1].config(fg="black", bg=Label_Colors[ijk_col])
        entrys_2[-1].delete(0, Tk.END)
        entrys_2[-1].insert(0, "Placeholder")
        ii += 1

    ijk_col = 2
    if(label_flag == 1):
        Tk.Label(framex, text=Labels[ijk_col]).grid(row=0, column=ijk_col)
    if(label_flag== 2):
        xy3 = Tk.Label(framex, text=Labels[ijk_col], font=Label_Font)
        xy3.grid(row=0, column=ijk_col)
        xy3.config(width=Widths[ijk_col], borderwidth="1", background=Label_Colors[ijk_col])
    entrys_3 = []
    variables_3 = []
    ii = 0
    for row in range(list_length):
        rowx = row + 1
        variables_3.append(StringVar())
        entrys_3.append(Tk.Entry(framex, textvariable=variables_3[ii], font=Entry_Font))
        entrys_3[-1].config(width=Widths[ijk_col], borderwidth="1")
        entrys_3[-1].grid(row=rowx, column=ijk_col)
        entrys_3[-1].config(fg="black", bg=Label_Colors[ijk_col])
        entrys_3[-1].delete(0, Tk.END)
        entrys_3[-1].insert(0, "Placeholder")
        ii += 1

def onFrameConfigure(canvas):
    '''Reset the scroll region to encompass the inner frame'''
    canvas.configure(scrollregion=canvas.bbox("all"))
#-----------------------------------------------------------------------------
root = Tk.Tk()
root.title("StackOverflow_Label_Entry_Align")
#---------------------------------
# set up canvas that scrolls for entries, header on separate canvas that does not scroll

canvas1 = Tk.Canvas(root, borderwidth=0, background="#ffffff", height=400, width=canvas_width)
frame1 = Tk.Frame(canvas1, background="#ffffff")

xscrollbar = Tk.Scrollbar(root, orient=Tk.HORIZONTAL, command=canvas1.xview)
yscrollbar = Tk.Scrollbar(root, orient=Tk.VERTICAL, command=canvas1.yview)

yscrollbar.pack(side=Tk.RIGHT, fill=Tk.Y)
xscrollbar.pack(side=Tk.BOTTOM, fill=Tk.X)

canvas1.configure(xscrollcommand=xscrollbar.set)
canvas1.configure(yscrollcommand=yscrollbar.set)

canvas1.pack(side="bottom", fill="both", expand=True)
canvas1.create_window((4,4), window=frame1, anchor="nw")

frame1.bind("<Configure>", lambda event, canvas=canvas1: onFrameConfigure(canvas))

def _on_mousewheel(event):
    canvas1.yview_scroll(-1*(event.delta/120), "units")
canvas1.configure(yscrollincrement='20') # adjust sensitivity
canvas1.bind_all("<MouseWheel>", _on_mousewheel) # Oakley has a post on this

canvas1.focus_set() # key to making canvas bind to left, right keys
#-----------------------------------------------------------------------------
# set up separate canvas for header row that will not scroll

canvas2 = Tk.Canvas(root, borderwidth=0, background="#ffffff", height=header_height, width=canvas_width)
frame2 = Tk.Frame(canvas2, background="#ffffff")

canvas2.pack(side="top", fill="both", expand=True)
canvas2.create_window((0,0), window=frame2, anchor="nw", height=header_height)

x1 = Tk.Label(frame2, text=Labels[0], font=Label_Font)
x1.grid(row=0, column=0)
x1.config(width=Widths[0], borderwidth="1", background=Label_Colors[0])

x2 = Tk.Label(frame2, text=Labels[1], font=Label_Font)
x2.grid(row=0, column=1)
x2.config(width=Widths[1], borderwidth="1", background=Label_Colors[1])

x3 = Tk.Label(frame2, text=Labels[2], font=Label_Font)
x3.grid(row=0, column=2)
x3.config(width=Widths[2], borderwidth="1", background=Label_Colors[2])
#-----------------------------------------------------------------------------
populate1(frame1) # add entry boxes
#-----------------------------------------------------------------------------
root.mainloop()
#-----------------------------------------------------------------------------

Post a Comment for "Freezing The Header In A Grid With A Scrollbar In Tkinter"