So I programmed a code which generates a window with a button on top and a small frame below. you click on the button on top and another button is created in col 0 row 0 in the small frame below, you click on it again and another button is created in col 0 row 1, so far so good that all works.
What I want to do now is to add a scrollbar such that, if the rows displayed in the small frame below become all filled with buttons, I can scroll down to the buttons added further down. The general question is thus: how can I add a scrollbar to a frame containing created buttons, organized via grid()?
I already tried to grid the created buttons in a frame, and to set that frame into a canvas, and then add the scrollbar onto that canvas, but nothing worked. (In the code below, take out the four lines of the scrollbar widget and you'll see how the program works. the addition of the four lines weirdly makes anything else disappear).
## import required modules ##
from tkinter import *
from functools import partial
## create window ##
window = Tk()
window.geometry("800x800")
## create global frame, which contains a canvas, which in turn contains ##
## the frame containing the grid of the products. This needs to be done ##
## in this way to be able to add a scrollbar aside the products grid ##
Globalframe = Frame(window, height = 210, width = 725)
Globalframe.place(relx = 0.03, rely = 0.54475)
Canv = Canvas(Globalframe, height = 198, width = 712)
Canv.place(relx = 0.00, rely = 0.01)
field = Frame(Canv, height = 186, width = 699)
field.place(relx = 0.00, rely = 0.01)
## add a scrollbar & configure it ##
scroll = Scrollbar(Globalframe, orient = VERTICAL)
scroll.config(command=Canv.yview)
Canv.config(yscrollcommand=scroll.set)
scroll.pack(side=RIGHT, fill=Y)
## create ADD button; function mentioned in command must be defined first ##
# predefine manufacturer variables & lists #
RIGOLVar = IntVar()
DataApexVar = IntVar()
DANIVar = IntVar()
Manufacturers = [["APPLE",RIGOLVar],["BANANA",DataApexVar],["PINEAPPLE",DANIVar]]
# predefine product variables & lists --> Put Lists into respective loops #
# because otherwise the system sends an unknown function error, which is #
# why the lists have been placed below the defined functions in the loop #
L31 = IntVar()
L32 = IntVar()
L34 = IntVar()
ListRIG=[["RED",L31],["GREEN",L32],["BROWN",L34]]
C801 = IntVar()
C82 = IntVar()
ListDAP =[["YELLOW",C801],["BROWN",C82]]
MS = IntVar()
HS = IntVar()
DHS = IntVar()
ListDAN=[["FRESH",MS],["INTERMEDIATE",HS],["OLD",DHS]]
# predefine amount variables & lists & directly sum it all up in one function (always the same) #
ONE = IntVar()
TWO = IntVar()
THREE = IntVar()
FOUR = IntVar()
FIVE = IntVar()
SIX = IntVar()
SEVEN = IntVar()
EIGHT = IntVar()
NINE = IntVar()
TEN = IntVar()
ELEVEN = IntVar()
TWELVE = IntVar()
THIRTEEN = IntVar()
FOURTEEN = IntVar()
FIFTEEN = IntVar()
SIXTEEN = IntVar()
SEVENTEEN = IntVar()
EIGHTEEN = IntVar()
NINETEEN = IntVar()
TWENTY = IntVar()
Varlist = [ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN,ELEVEN,TWELVE,THIRTEEN,FOURTEEN,FIFTEEN,SIXTEEN,SEVENTEEN,EIGHTEEN,NINETEEN,TWENTY]
Amlist = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
def detamount(Var):
for x in Varlist:
if x == Var:
x.set(1)
else:
x.set(0)
def OPAM(p):
a = StringVar()
for y in Merlist:
if y[1].get() == 1:
a = str(y[0])
else:
pass
for label in field.grid_slaves(row=p,column=6):
label.grid_forget()
mine = Label(field,text =a+"x",bg="cyan")
mine.grid(row=p, column = 6)
def F1(p):
detamount(ONE)
OPAM(p)
def F2(p):
detamount(TWO)
OPAM(p)
def F3(p):
detamount(THREE)
OPAM(p)
def F4(p):
detamount(FOUR)
OPAM(p)
def F5(p):
detamount(FIVE)
OPAM(p)
def F6(p):
detamount(SIX)
OPAM(p)
def F7(p):
detamount(SEVEN)
OPAM(p)
def F8(p):
detamount(EIGHT)
OPAM(p)
def F9(p):
detamount(NINE)
OPAM(p)
def F10(p):
detamount(TEN)
OPAM(p)
def F11(p):
detamount(ELEVEN)
OPAM(p)
def F12(p):
detamount(TWELVE)
OPAM(p)
def F13(p):
detamount(THIRTEEN)
OPAM(p)
def F14(p):
detamount(FOURTEEN)
OPAM(p)
def F15(p):
detamount(FIFTEEN)
OPAM(p)
def F16(p):
detamount(SIXTEEN)
OPAM(p)
def F17(p):
detamount(SEVENTEEN)
OPAM(p)
def F18(p):
detamount(EIGHTEEN)
OPAM(p)
def F19(p):
detamount(NINETEEN)
OPAM(p)
def F20(p):
detamount(TWENTY)
OPAM(p)
Famlist = [F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12,F13,F14,F15,F16,F17,F18,F19,F20]
Merlist = []
for i in range(20):
Merlist.append([Amlist[i],Varlist[i],Famlist[i]])
def askamount(p):
ab= Menubutton(field, text="Amount ?")
ab.menu = Menu(ab)
ab["menu"] = ab.menu
for i in Merlist:
ab.menu.add_checkbutton(label=i[0],variable = i[1],command=partial(i[2],p))
ab.grid(row=p, column = 5)
# function - Add Remove button, Manufacturer Label, & product dropdown menu #
# Define function to create product's output (functionalized because always same logic) #
def OPProd(li,co,p):
n = StringVar()
for s in li:
if s[1].get() == 1:
n = str(s[0])
else:
pass
for label in field.grid_slaves(row=r,column=co):
label.grid_forget()
mylab = Label(field,text=n,bg="green")
mylab.grid(row=p,column=co)
# Determine the picked Manufacturer and print the corresponding label to the right #
def setMANVar(V):
for b in Manufacturers:
if b[1] == V:
b[1].set(1)
else:
b[1].set(0)
def OPMAN(p):
c = StringVar()
for d in Manufacturers:
if d[1].get()==1:
c = str(d[0])
else:
pass
# Delete any previously created labels, to have the correct one for the end #
for label in field.grid_slaves(row=p,column=2):
label.grid_forget()
# Create the Label of the correct Manufacturer #
myl = Label(field,text=c,bg="green")
myl.grid(row=p,column=2)
# Create the next general checkbutton for the products #
pb = Menubutton(field,text = "Product ?")
pb.grid(row=p,column=3)
pb.menu = Menu(pb, tearoff=0)
pb["menu"] = pb.menu
# Create the dropdown-list of the product's button just created, in function of the picked manufacturer #
for label in field.grid_slaves(row=p,column=2):
if label["text"]=="APPLE":
def setRIGVar(Var):
for m in ListRIG:
if m[1] == Var:
m[1].set(1)
else:
m[1].set(0)
def FL31(p):
setRIGVar(L31)
OPProd(ListRIG,4,p)
askamount(p)
def FL32(p):
setRIGVar(L32)
OPProd(ListRIG,4,p)
askamount(p)
def FL34(p):
setRIGVar(L34)
OPProd(ListRIG,4,p)
askamount(p)
ListFRIG=[FL31,FL32,FL34]
for i in range(len(ListRIG)):
ListRIG[i].append(ListFRIG[i])
for i in ListRIG:
pb.menu.add_checkbutton(label=i[0],variable=i[1],command=partial(i[2],p))
elif label["text"]=="BANANA":
def setDAPVar(Var):
for m in ListDAP:
if m[1] == Var:
m[1].set(1)
else:
m[1].set(0)
def F801(p):
setDAPVar(C801)
OPProd(ListDAP,4,p)
askamount(p)
def F82(p):
setDAPVar(C82)
OPProd(ListDAP,4,p)
askamount(p)
ListFDAP=[F801,F82]
for i in range(len(ListDAP)):
ListDAP[i].append(ListFDAP[i])
for i in ListDAP:
pb.menu.add_checkbutton(label=i[0],variable=i[1],command=partial(i[2],p))
elif label["text"]=="PINEAPPLE":
def setDANVar(Var):
for m in ListDAN:
if m[1] == Var:
m[1].set(1)
else:
m[1].set(0)
def FMS(p):
setDANVar(MS)
OPProd(ListDAN,4,p)
askamount(p)
def FHS(p):
setDANVar(HS)
OPProd(ListDAN,4,p)
askamount(p)
def FDHS(p):
setDANVar(DHS)
OPProd(ListDAN,4,p)
askamount(p)
ListFDAN = [FMS,FHS,FDHS]
for i in range(len(ListDAN)):
ListDAN[i].append(ListFDAN[i])
for i in ListDAN:
pb.menu.add_checkbutton(label=i[0],variable=i[1],command=partial(i[2],p))
# Manufacturer Functions which set only the picked Manufacturer-Checkbutton = 1 and print the OP - Label #
def FRIG(p):
setMANVar(RIGOLVar)
OPMAN(p)
def FDAP(p):
setMANVar(DataApexVar)
OPMAN(p)
def FDAN(p):
setMANVar(DANIVar)
OPMAN(p)
## Define Clearline Function, associated to the remove button ##
def Clearline(p):
global num
for label in field.grid_slaves(row=p,column=0):
clickedbutton = label
for label in field.grid_slaves(row=p):
label.grid_forget()
remove_button_IDs.remove(clickedbutton)
for button in remove_button_IDs:
button.configure(text="- Remove Product #" + str(remove_button_IDs.index(button)+1))
num -= 1
## Create function associated to the ADD, starting up with the Remove and Manuf Button on a new row ##
r = -1
num = 0
remove_button_IDs = []
def AddProduct():
global r
global num
r += 1
num += 1
button = Button(field,text = "- Remove Product #" + str(num), bg = "red",command = partial(Clearline,r))
button.grid(row=r,column=0)
remove_button_IDs.append(button)
mb = Menubutton(field,text = "What ?")
mb.grid(row=r,column=1)
mb.menu = Menu(mb,tearoff=0)
mb["menu"] = mb.menu
ListFManufacturers = [FRIG,FDAP,FDAN]
for i in range(len(Manufacturers)):
Manufacturers[i].append(ListFManufacturers[i])
for i in Manufacturers:
mb.menu.add_checkbutton(label=i[0],variable=i[1],command= partial(i[2],r))
## Create ADD Button ##
A = Button(window, text = "+ Add a new product",command = AddProduct)
A.place(relx = 0.2, rely = 0.01)
correction from upon line 15:
def update_scrollregion(event):
Canv.configure(scrollregion=Canv.bbox("all"))
Globalframe = Frame(window, height = 210, width = 725)
Globalframe.place(relx = 0.03, rely = 0.54475)
Canv = Canvas(Globalframe, height = 198, width = 712)
Canv.grid(row=0,column=0,sticky="nsew")
field = Frame(Canv)
Canv.create_window(0,0,window=field,anchor="nw")
## add a scrollbar & configure it ##
s = Scrollbar(Globalframe, orient=VERTICAL)
s.config(command=Canv.yview)
Canv.config(yscrollcommand=s.set)
s.grid(row=0, column=1, sticky="ns")
field.bind("<Configure>",update_scrollregion)
I found this option thanks to j_4321 (Tkinter canvas & scrollbar with grid) GREAT THANKS TO THIS GUY!