Home > Mobile >  how to implement a scrollbar to grid in tkinter
how to implement a scrollbar to grid in tkinter

Time:05-01

I have a hard time implementing a scrollbar into my Tkinter project. I've been through numerous articles and answered questions on how to implement a scrollbar, but I'm just unable to implement a working solution after an entire day of researching this one 'simple' matter.

my current code looks like this:

import tkinter as tk
from tkinter import Button, ttk
from PIL import ImageTk, Image
from functools import partial
import queue as qu
import math
import re
import os

window = tk.Tk()

queue = qu.Queue()

#Basic values
#the window size
windowSize = "700x1000"
#picture and container size
x, y = 200, 300
#tmp
sidepanelsize = 200

window.geometry(windowSize)

#button identifier
def change(i):
    print(I)

#temporary content generator
for g in range(12):
    for item in os.listdir("."):
        if re.search(r"\.(jpg|png)$", item):
            queue.put(item)

n = queue.qsize()

#other panels that are going to be used later
frameLeft = tk.Frame(master=window, width=sidepanelsize, relief=tk.RIDGE)
frameLeft.pack(fill=tk.Y, side=tk.LEFT)

label1 = tk.Label(master=frameLeft, text="Left Panel")
label1.pack()

buttonLeft1 = tk.Button(master=frameLeft, text="Button 1", command=lambda: print("I'm a side button!"))
buttonLeft1.pack()

frameMain = tk.Frame(master=window, relief=tk.GROOVE, borderwidth=1)
frameMain.pack(side=tk.TOP, fill=tk.X, expand=1)


# SCROLLBAR IF YOU DISABLE THIS SECTION AND PUTS SOME PICTURES IN THE FOLDER WHITH THE FILE THE CODE WORKS #
myCanvas = tk.Canvas(master=frameMain)
myCanvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)

myScrollbar = ttk.Scrollbar(master=frameMain, orient=tk.VERTICAL, command=myCanvas.yview)
myScrollbar.pack(side=tk.RIGHT, fill=tk.Y)

myCanvas.configure(yscrollcommand=myScrollbar.set)
myCanvas.bind('<Configure>', lambda e: myCanvas.configure(scrollregion=myCanvas.bbox("all")))

secondFrame = tk.Frame(master=myCanvas)
myCanvas.create_window((0, 0), window=secondFrame, anchor=tk.NW)
############################ END OF SCROLLBAR ############################


noOfImgPerRow = math.floor((int(windowSize.split("x")[0])-sidepanelsize 100)/x)
imgs = []

#generates the grid
for i in range(n):
    o = i
    i = (o % noOfImgPerRow)   1
    j = math.floor(o/noOfImgPerRow)   1

    frameMain.columnconfigure(i, weight = 1, minsize=x 15)
    frameMain.rowconfigure(i, weight = 1, minsize=y 50)


    frameBox = tk.Frame(
        master=frameMain,
        relief=tk.RAISED,
        borderwidth=1,
        width = x,
        height = y
    )
    # here the error refferences to
    frameBox.grid(row=j, column=i, padx=5, pady=5)

    img = Image.open(queue.get()).convert("RGBA")
    width, height = img.size

    if width/x >= height/y:
        left  = width/2-(round((height*x)/y))/2
        right = width/2 (round((height*x)/y))/2
        upper = 0
        lower = height
    else:
        left  = 0
        right = width
        upper = height/2-(round((width*y)/x))/2
        lower = height/2 (round((width*y)/x))/2

    img2 = img.crop([left, upper, right, lower])
    img2 = img2.resize((x, y), Image.Resampling.LANCZOS)
    imgs.append(ImageTk.PhotoImage(img2))
    label = tk.Label(master = frameBox, image = imgs[-1])
    label.pack()
    mainButton = Button(master=frameBox, text="Start", command=partial(change, o))
    mainButton.pack()

window.mainloop()

I've tried to highlight the only thing of concern, that being the scrollbar, everything else is working at the moment, I just wanted to post the whole code for better understanding if it would help in any way.

My problem is whenever I implement the scrollbar, it throws back an error stating:

Traceback (most recent call last):
  File "e:\Python\starter\main.py", line 85, in <module>
    frameBox.grid(row=j, column=i, padx=5, pady=5)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\tkinter\__init__.py", line 2522, in grid_configure
    self.tk.call(
_tkinter.TclError: cannot use geometry manager grid inside .!frame2 which already has slaves managed by pack

This error seems pretty self-explanatory, just grid the canvas instead of packing it, but when after a lot of small tweaking and doing things a roundabouts things

My second thought was if it has a problem with the grid to wrap the gridded frame in another bigger packed frame, like so:

yetAnotherFrame = tk.Frame(frameMain)
yetAnotherFrame.pack()

noOfImgPerRow = math.floor((int(windowSize.split("x")[0])-sidepanelsize 100)/x)
imgs = []

for i in range(n):
    o = i
    i = (o % noOfImgPerRow)   1
    j = math.floor(o/noOfImgPerRow)   1

    yetAnotherFrame.columnconfigure(i, weight = 1, minsize=x 15)
    yetAnotherFrame.rowconfigure(i, weight = 1, minsize=y 50)


    frameBox = tk.Frame(
        master=yetAnotherFrame,
        relief=tk.RAISED,
        borderwidth=1,
        width = x,
        height = y
    )
    frameBox.grid(row=j, column=i, padx=5, pady=5)

This actually runs to my surprise, but the scrollbar still isn't working and the layout is broken again.

CodePudding user response:

Solution

In your code frameBox's parent is frameMain. Instead you need to have the canvas as parent or the secondFrame which have the canvas as its parent.

Example

This is basically your code with fixes, but some of the unnecessary parts are removed.

import tkinter as tk
from tkinter import ttk

window = tk.Tk()
window.geometry("400x400")

frameLeft = tk.Frame(master=window, width=400, height=400, relief=tk.RIDGE)
frameLeft.pack(fill=tk.Y, side=tk.LEFT)

myCanvas = tk.Canvas(master=frameLeft)
myCanvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)

myScrollbar = ttk.Scrollbar(master=frameLeft, orient=tk.VERTICAL, command=myCanvas.yview)
myScrollbar.pack(side=tk.RIGHT, fill=tk.Y)

myCanvas.configure(yscrollcommand=myScrollbar.set)
myCanvas.bind('<Configure>', lambda e: myCanvas.configure(scrollregion=myCanvas.bbox("all")))

secondFrame = tk.Frame(master=myCanvas)
myCanvas.create_window((0, 0), window=secondFrame, anchor=tk.NW)

for i in range(100):
    lbl = tk.Label(secondFrame, text=f"Label {i}")
    lbl.grid(column=0, row=i, sticky=tk.W)

window.mainloop()
  • Related