Tkinter - Struggling to Add a Frame Which is Scrollable

48 Views Asked by At

I'm trying to create a system that allows you to add checks into a profile using a gui. I want the checks to be in their own frame that has a scroll wheel because I don't know how many checks a profile will need, but don't want to expand the size of the window. I've been trying to follow an older post about creating a scrollbar just for a grid but can't quite seem to implement their solutions into my own code.

I've also run into a problem where in one configuration, the button to add a check will disappear and the frame containing the checks will appear, or vice versa. I'm unable to have the two displaying at the same time.

Here is the code I've written:

from tkinter import *


gCheckList = []
tCheckList = []

def addCheck(type, homeFrame):
    '''adds a new row to the profile editor which allows the user to input a check'''
    if type == 'g':
        checkLength = 14
        listLength = len(gCheckList)
    elif type == 't':
        checkLength = 9
        listLength = len(tCheckList)
    else:
        checkLength = 0
    
    checkFrame = Frame(homeFrame)
    checkFrame.grid(row=2 + listLength,column=0,padx=10,pady=5)

    checkName = Text(checkFrame, width=10, height=1, font=('Courier',12))
    checkName.grid(row=0, column=0,padx=10,pady=5)

    if type == 'g':
        gCheckList.append(checkFrame)
    elif type == 't':
        tCheckList.append(checkFrame)
    else:
        return
    
    homeFrame.update_idletasks()

    return

#creates root
root = Tk()
root.title = 'Profile Editor'
root.geometry('750x300+250+225')

#creates profile name frame
profileNameFrame = Frame(root)
profileNameFrame.grid(row=0,column=0,padx=10,pady=5)

#Creates a spot to name the profile
nameLabel = Label(profileNameFrame, text='Profile Name: ', font=('Courier',12))
nameLabel.grid(row=0,column=0,padx=10,pady=5)
nameText = Text(profileNameFrame, width=30, height=1, font=('Courier',12))
nameText.grid(row=0, column=1,padx=10,pady=5)

#Frame for title
titleFrame = Frame(root)
titleFrame.grid(row=1,column=0,padx=10,pady=5)
gCheckProfileLabel = Label(titleFrame, text='header', font=('Courier',14))
gCheckProfileLabel.grid(row=0,column=0,padx=10,pady=5)

#Frame for headers
headerFrame = Frame(titleFrame)
headerFrame.grid(row=1,column=0,padx=10,pady=5)
headerLabel = Label(headerFrame, text='Check Name  Indicator  '+''.join(['1  ', '2  ','3  ','4  ','5  ','6  ','7  ','8  ','9  ','10 ','11 ','12 ','13 ','14 ']), font=('Courier',12))
headerLabel.grid(row=0,column=0,padx=10,pady=5)

#frame for canvas
checkFrame = Frame(titleFrame)
checkFrame.grid(row=2,column=0,padx=5,pady=5, sticky='nw')
checkFrame.grid_rowconfigure(0,weight=1)
checkFrame.grid_columnconfigure(0,weight=1)
checkFrame.grid_propagate(False) #don't know if this is needed

#canvas for scrollbar
scrollCanvas = Canvas(checkFrame, bg='yellow')
scrollCanvas.grid(row=0,column=0,sticky='news')

#link scrollbar to canvas
gScrollbar = Scrollbar(checkFrame, orient='vertical',command=scrollCanvas.yview)
gScrollbar.grid(row=0,column=1,sticky='ns')
scrollCanvas.configure(yscrollcommand=gScrollbar.set)

#Create frame to contain checks
gChecksFrame = Frame(scrollCanvas, bg='blue')
scrollCanvas.create_window((0,0),window=gChecksFrame, anchor='nw')
gChecksFrame.grid(row=1,column=1,padx=10,pady=5)

#Creates a new check row to be added into the program
addGCheckButton = Button(titleFrame, text='+', font=('Courier',14), command=lambda: addCheck('g', gChecksFrame))
addGCheckButton.grid(row=0,column=2,padx=1,pady=5)

root.mainloop()
1

There are 1 best solutions below

0
On BEST ANSWER

There are following issues in your code:

  • called checkFrame.grid_propagate(False) which causes the frame not be resized to fit child widgets and so its size will be 1x1 by default.
  • called gChecksFrame.grid(...) which causes the canvas be resized to fit the frame. gChecksFrame has already been put into the canvas using .create_window(...).
  • not update scrollregion of the canvas when gChecksFrame is resized, so the scrollbar will not be activated.

Below are the required changes:

...
# don't call .grid_propagate(False)
#checkFrame.grid_propagate(False)
...
# don't call gChecksFrame.grid(...)
#gChecksFrame.grid(row=1,column=1,padx=10,pady=5)

# update canvas scrollregion whenever gChecksFrame is resized
gChecksFrame.bind("<Configure>", lambda e: scrollCanvas.config(scrollregion=scrollCanvas.bbox("all")))
...

Suggest to remove root.geometry(...) as well because the window is not tall enough to show all the widgets.