Using Python and tkinter I have created a dummy app with a scrollable frame. There are two column headings in a container frame. The container frame also contains a canvas. Inside the canvas is an inner frame with two columns of scrollable content.
Problem: the column headings do not align with the columns, presumably because the columns in the container frame and the inner frame do not align. But, as far as I can see, I have configured the columns exactly the same way in both frames. Can anyone give me a clue as to what I am overlooking?
The code is below. I cannot claim credit for all of the code, as I was following a tutorial from Tutorialspoint (), but the tutorial only worked with a single column of scrollable data where the heading scrolled with the data. I changed this so the heading stays put when you scroll and I needed 2 columns of data.
N.B. If I uncomment the line starting innerframe.grid(..., the columns match up (almost), but the scrollbar disappears.
import tkinter as tk
from tkinter import ttk
def _on_mousewheel(event):
scrollcanvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
root=tk.Tk()
root.title("Scrollable Grid Example")
# Create outer Frame for Grid Layout
outerframe = ttk.Frame(root)
outerframe.grid(row=0, column=0, columnspan=2, sticky="nsew")
# Create a Canvas and Scrollbar
scrollcanvas = tk.Canvas(outerframe)
scrollbar = ttk.Scrollbar(outerframe, orient="vertical",command=scrollcanvas.yview)
scrollcanvas.configure(yscrollcommand=scrollbar.set)
# Create label to outer frame/canvas so that it stays put when rows below scroll.
label = tk.Label(outerframe, text="Scrollable Buttons", width=20)
label.grid(row=0, column=0, pady=5, sticky="w")
label1 = tk.Label(outerframe, text="Scrollable Text", width=20)
label1.grid(row=0, column=1, pady=5, sticky="w")
# Create inner Frame for Scrollable Content
innerframe=ttk.Frame(scrollcanvas)
# Set binding to adjusts the canvas scroll region when size of the inner frame changes
innerframe.bind( "<Configure>", lambda e: scrollcanvas.configure( scrollregion=scrollcanvas.bbox("all") ) )
# Add labels and buttons to the Content Frame
for i in range(0, 20):
button=ttk.Button(innerframe, text=f"Button {i}", width=20)
button.grid(row=i, column=0, pady=5, sticky="w" )
label2 = tk.Label(innerframe, text=f"Text Line {i}", width=20)
label2.grid(row=i, column=1, pady=5, sticky="w")
# Ensure the window and components expand proportionally when resized.
root.rowconfigure(0, weight=1)
outerframe.rowconfigure(0, weight=1)
innerframe.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
outerframe.columnconfigure(0, weight=1)
outerframe.columnconfigure(1, weight=1)
innerframe.columnconfigure(0, weight=1)
innerframe.columnconfigure(1, weight=1)
# Place the canvas and scrollbar onto the window, with the scrollbar adjacent to the canvas
scrollcanvas.create_window((0, 0), window=innerframe, anchor="nw")
scrollcanvas.grid(row=1, column=0, columnspan=2, sticky="nsew")
# innerframe.grid(row=1, column=0, columnspan=2, sticky="nw")
scrollbar.grid(row=1, column=2, sticky="ns")
# Bind the Canvas to Mousewheel Events
scrollcanvas.bind_all("<MouseWheel>", _on_mousewheel)
# Run the Tkinter Event Loop
root.mainloop()
def _on_mousewheel(event):
scrollcanvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
Using Python and tkinter I have created a dummy app with a scrollable frame. There are two column headings in a container frame. The container frame also contains a canvas. Inside the canvas is an inner frame with two columns of scrollable content.
Problem: the column headings do not align with the columns, presumably because the columns in the container frame and the inner frame do not align. But, as far as I can see, I have configured the columns exactly the same way in both frames. Can anyone give me a clue as to what I am overlooking?
The code is below. I cannot claim credit for all of the code, as I was following a tutorial from Tutorialspoint (https://www.tutorialspoint/implementing-a-scrollbar-using-grid-manager-on-a-tkinter-window), but the tutorial only worked with a single column of scrollable data where the heading scrolled with the data. I changed this so the heading stays put when you scroll and I needed 2 columns of data.
N.B. If I uncomment the line starting innerframe.grid(..., the columns match up (almost), but the scrollbar disappears.
import tkinter as tk
from tkinter import ttk
def _on_mousewheel(event):
scrollcanvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
root=tk.Tk()
root.title("Scrollable Grid Example")
# Create outer Frame for Grid Layout
outerframe = ttk.Frame(root)
outerframe.grid(row=0, column=0, columnspan=2, sticky="nsew")
# Create a Canvas and Scrollbar
scrollcanvas = tk.Canvas(outerframe)
scrollbar = ttk.Scrollbar(outerframe, orient="vertical",command=scrollcanvas.yview)
scrollcanvas.configure(yscrollcommand=scrollbar.set)
# Create label to outer frame/canvas so that it stays put when rows below scroll.
label = tk.Label(outerframe, text="Scrollable Buttons", width=20)
label.grid(row=0, column=0, pady=5, sticky="w")
label1 = tk.Label(outerframe, text="Scrollable Text", width=20)
label1.grid(row=0, column=1, pady=5, sticky="w")
# Create inner Frame for Scrollable Content
innerframe=ttk.Frame(scrollcanvas)
# Set binding to adjusts the canvas scroll region when size of the inner frame changes
innerframe.bind( "<Configure>", lambda e: scrollcanvas.configure( scrollregion=scrollcanvas.bbox("all") ) )
# Add labels and buttons to the Content Frame
for i in range(0, 20):
button=ttk.Button(innerframe, text=f"Button {i}", width=20)
button.grid(row=i, column=0, pady=5, sticky="w" )
label2 = tk.Label(innerframe, text=f"Text Line {i}", width=20)
label2.grid(row=i, column=1, pady=5, sticky="w")
# Ensure the window and components expand proportionally when resized.
root.rowconfigure(0, weight=1)
outerframe.rowconfigure(0, weight=1)
innerframe.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
outerframe.columnconfigure(0, weight=1)
outerframe.columnconfigure(1, weight=1)
innerframe.columnconfigure(0, weight=1)
innerframe.columnconfigure(1, weight=1)
# Place the canvas and scrollbar onto the window, with the scrollbar adjacent to the canvas
scrollcanvas.create_window((0, 0), window=innerframe, anchor="nw")
scrollcanvas.grid(row=1, column=0, columnspan=2, sticky="nsew")
# innerframe.grid(row=1, column=0, columnspan=2, sticky="nw")
scrollbar.grid(row=1, column=2, sticky="ns")
# Bind the Canvas to Mousewheel Events
scrollcanvas.bind_all("<MouseWheel>", _on_mousewheel)
# Run the Tkinter Event Loop
root.mainloop()
def _on_mousewheel(event):
scrollcanvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
Share
Improve this question
edited Mar 3 at 14:55
sg-gs2025
asked Mar 3 at 13:52
sg-gs2025sg-gs2025
234 bronze badges
1 Answer
Reset to default 2It is because the innerframe
does not have the same width of the canvas (which is the sum of the widths of the two labels at the top).
You need to:
- set
highlightthickness=0
intk.Canvas(...)
- set the width of
innerframe
to the same asscrollcanvas
(in callback of<Configure>
event onscrollcanvas
) - add
uniform=...
tooutframe.columnconfigure(...)
andinnerframe.columnconfigure(...)
to make sure the two columns inside bothouterframe
andinnerframe
have same width
Updated code:
import tkinter as tk
from tkinter import ttk
def _on_mousewheel(event):
scrollcanvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
root=tk.Tk()
root.title("Scrollable Grid Example")
# Create outer Frame for Grid Layout
outerframe = ttk.Frame(root)
outerframe.grid(row=0, column=0, columnspan=2, sticky="nsew")
# Create a Canvas and Scrollbar
scrollcanvas = tk.Canvas(outerframe, highlightthickness=0)
scrollbar = ttk.Scrollbar(outerframe, orient="vertical",command=scrollcanvas.yview)
scrollcanvas.configure(yscrollcommand=scrollbar.set)
# Create label to outer frame/canvas so that it stays put when rows below scroll.
label = tk.Label(outerframe, text="Scrollable Buttons", width=20, bd=1, relief='raised')
label.grid(row=0, column=0, pady=5, sticky="w")
label1 = tk.Label(outerframe, text="Scrollable Text", width=20, bd=1, relief='raised')
label1.grid(row=0, column=1, pady=5, sticky="w")
# Create inner Frame for Scrollable Content
innerframe=tk.Frame(scrollcanvas, bg='gray80')
# Set binding to adjusts the canvas scroll region when size of the inner frame changes
innerframe.bind( "<Configure>", lambda e: scrollcanvas.configure( scrollregion=scrollcanvas.bbox("all") ) )
# Add labels and buttons to the Content Frame
for i in range(0, 20):
button=ttk.Button(innerframe, text=f"Button {i}", width=20)
button.grid(row=i, column=0, pady=5, sticky="w" )
label2 = tk.Label(innerframe, text=f"Text Line {i}", width=20, bd=1, relief='solid')
label2.grid(row=i, column=1, pady=5, sticky="w")
# Ensure the window and components expand proportionally when resized.
root.rowconfigure(0, weight=1)
outerframe.rowconfigure(0, weight=1)
#innerframe.rowconfigure(0, weight=1) # this line is not necessary
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
outerframe.columnconfigure(0, weight=1, uniform=1) # added setting uniform option
outerframe.columnconfigure(1, weight=1, uniform=1)
innerframe.columnconfigure(0, weight=1, uniform=1)
innerframe.columnconfigure(1, weight=1, uniform=1)
# Place the canvas and scrollbar onto the window, with the scrollbar adjacent to the canvas
scrollcanvas.create_window((0, 0), window=innerframe, anchor="nw", tags=('inner')) # added tags
scrollcanvas.grid(row=1, column=0, columnspan=2, sticky="nsew")
scrollbar.grid(row=1, column=2, sticky="ns")
# Bind the Canvas to Mousewheel Events
scrollcanvas.bind_all("<MouseWheel>", _on_mousewheel)
# set width of innerframe to same of scrollcanvas
scrollcanvas.bind('<Configure>', lambda e: scrollcanvas.itemconfig('inner', width=e.width))
# Run the Tkinter Event Loop
root.mainloop()
Note that I have add borders to those labels in order to see the effect easily.
Result: