最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Aligning grid columns between parent and container frames using tkinter - Stack Overflow

programmeradmin0浏览0评论

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
Add a comment  | 

1 Answer 1

Reset to default 2

It 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 in tk.Canvas(...)
  • set the width of innerframe to the same as scrollcanvas (in callback of <Configure> event on scrollcanvas)
  • add uniform=... to outframe.columnconfigure(...) and innerframe.columnconfigure(...) to make sure the two columns inside both outerframe and innerframe 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:

发布评论

评论列表(0)

  1. 暂无评论