tkinter: using scrollbars on a canvas

68,961

Your scrollbars need to have the Frame as a parent, not the Canvas:

from tkinter import *
root=Tk()
frame=Frame(root,width=300,height=300)
frame.pack(expand=True, fill=BOTH) #.grid(row=0,column=0)
canvas=Canvas(frame,bg='#FFFFFF',width=300,height=300,scrollregion=(0,0,500,500))
hbar=Scrollbar(frame,orient=HORIZONTAL)
hbar.pack(side=BOTTOM,fill=X)
hbar.config(command=canvas.xview)
vbar=Scrollbar(frame,orient=VERTICAL)
vbar.pack(side=RIGHT,fill=Y)
vbar.config(command=canvas.yview)
canvas.config(width=300,height=300)
canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
canvas.pack(side=LEFT,expand=True,fill=BOTH)

root.mainloop()

The reason why this works is due to how pack works. By default it will attempt to shrink (or grow) a container to exactly fit its children. Because the scrollbars are children of the canvas in the original example, the canvas shrinks to fit.

If you want the scrollbars to appear inside the canvas, the trick is to use an extra frame. Place the canvas and scrollbars in this inner frame, turn the borders off of the canvas and on for the frame. Set the background of the frame to be the same as the canvas and it will appear that the scrollbars are inside the canvas.

Share:
68,961
Robert Mastragostino
Author by

Robert Mastragostino

Updated on January 26, 2022

Comments

  • Robert Mastragostino
    Robert Mastragostino over 2 years

    I'm trying to make a canvas scrollable. However, once I try to set up scrollbars to work with the canvas, tkinter seems to completely ignore the dimensions I initially set for my canvas. I've tried packing them all in a frame, setting the canvas to fill the frame and then setting the frame size, but that presents the same problem unless I set the frame to fill the window as well, which isn't what I want. Basically, I want a fixed-size canvas with scrollbars on it. My current code looks like this (in python 3.1):

    from tkinter import *
    root=Tk()
    frame=Frame(root,width=300,height=300)
    frame.grid(row=0,column=0)
    canvas=Canvas(frame,bg='#FFFFFF',width=300,height=300,scrollregion=(0,0,500,500))
    hbar=Scrollbar(canvas,orient=HORIZONTAL)
    hbar.pack(side=BOTTOM,fill=X)
    hbar.config(command=canvas.xview)
    vbar=Scrollbar(canvas,orient=VERTICAL)
    vbar.pack(side=RIGHT,fill=Y)
    vbar.config(command=canvas.yview)
    canvas.config(width=300,height=300)
    canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
    canvas.pack(side=LEFT,expand=True,fill=BOTH)
    
    root.mainloop()
    
  • Lanfear
    Lanfear over 8 years
    Why must we pack the canvas last? I've noticed that when I pack the canvas before any of the scrollbars it ends up overlapping the horizontal scrollbar - I just started using tkinter, but I thought that pack wouldn't overlap stuff. Thanks
  • ch271828n
    ch271828n over 7 years
    And the core is scrollregion=(0,0,500,500), without it the whole can't work!!
  • Korzak
    Korzak about 5 years
    @Bryan Oakley, why do you set the width and height of the canvas with .config when you already set them when you created it with canvas=Canvas(...)?
  • Russell Smith
    Russell Smith about 5 years
    @Korzak: I don't know. I didn't write that code. There's no reason to do it.
  • Korzak
    Korzak about 5 years
    Okay thanks. @BryanOakley do you know why when I used this code, it only will scroll down and not up with the mousewheel? I can post in a new question if that's best. Thanks.
  • Russell Smith
    Russell Smith about 5 years
    @Korzak: that should be a new question. Before you ask, search this site for similar questions. There are several questions related to the mousewheel on this site.
  • MathanKumar
    MathanKumar almost 4 years
    is there any option to show scrollbar inside canvas region using grid() ?
  • typedecker
    typedecker about 3 years
    For some reason when I use this script with bindings to the canvas like that of B1-Motion and all then it seems to have its event.x and event.y based on window coordinates and not canvas coordinates @BryanOakley do you know why that might be the case.
  • typedecker
    typedecker about 3 years
    Ok nevermind so for anyone that might have the problem of window coordinates the window coords from event.x and event.y can be converted into canvas coords using the canvas.canvasx and canvas.canvasy functions of the canvas.
  • typedecker
    typedecker about 3 years
    Also I think instead of scrollregion = (0, 0, 500, 500), scrollregion = canvas.bbox('all') will be more general of a solution.
  • Flair
    Flair over 2 years
    If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From Review
  • Mote Zart
    Mote Zart almost 2 years
    Did U solve it? I have this issue also