【问题标题】:Why are the widgets stacked on top of each other in the tkinter canvas?为什么小部件在 tkinter 画布中堆叠在一起?
【发布时间】:2026-01-16 15:10:01
【问题描述】:

我一直在使用 tkinter 在 python 中创建一个程序,并且我有一个需要 滚动条 的下载标签。经过一些谷歌搜索后,我发现让一个完整的应用程序可滚动并不容易,因为只有几个小部件本身支持滚动条小部件。我搜索了它并找到了一种方法,但是,当我尝试自己实现它时,我遇到了一个错误。当我通过这种方法使用网格时,小部件会相互堆叠,而不是在单独的行上。我尝试像教程一样使用 for 循环做同样的事情,问题得到解决,但我不希望我的程序使用 for 循环。这是没有for循环的代码:

from tkinter import *
from tkinter import ttk

available_row = 0

root = Tk()
root.title("A Scrollbar")
root.geometry("580x308")
# Create a download frame
download_fr = Frame(root, width=580, height=308)
big_lbl = Label(download_fr, text="Just a BIG label. ", font=("Helvetica", "30", "bold"))
prg_br = ttk.Progressbar(download_fr, orient=HORIZONTAL, length=300, mode='indeterminate')



def pack_bars():
    global available_row
    big_lbl.pack_forget()
    prg_br.pack_forget()
    # Pack the Scrollbar
    my_scrollbar.pack(side=RIGHT, fill=Y)
    # Configure the canvas
    cnava.configure(yscrollcommand=my_scrollbar.set)
    cnava.bind("<Configure>", lambda e: cnava.configure(scrollregion=cnava.bbox("all")))
    # Create another frame inside the canvas
    second_frame = Frame(cnava)
    # Add that new frame to a window in the canvas
    cnava.create_window((0, 0), window=second_frame, anchor="nw")
    ttk.Button(second_frame, text=f"This is the {available_row}th button").grid(row=available_row, column=0)
    available_row += 1
    print(f"Next available row: {available_row}")


btn = Button(download_fr, text="Pack progress bars. ", command=pack_bars)
# Create a canvas
cnava = Canvas(download_fr, bg="red")
# Create a Scrollbar
my_scrollbar = ttk.Scrollbar(download_fr, orient=VERTICAL, command=cnava.yview)
big_lbl.pack(pady=(85, 5))
prg_br.pack()
prg_br.start(5)
cnava.pack(side=LEFT, fill=BOTH, expand=1)
btn.pack()
download_fr.pack(fill=BOTH, expand=1)
root.mainloop()

这是带有 for 循环的相同代码:

from tkinter import *
from tkinter import ttk


root = Tk()
root.title("A Scrollbar")
root.geometry("580x308")
# Create a download frame
download_fr = Frame(root, width=580, height=308)
big_lbl = Label(download_fr, text="Just a BIG label. ", font=("Helvetica", "30", "bold"))
prg_br = ttk.Progressbar(download_fr, orient=HORIZONTAL, length=300, mode='indeterminate')


def pack_bars():
    big_lbl.pack_forget()
    prg_br.pack_forget()
    # Pack the Scrollbar
    my_scrollbar.pack(side=RIGHT, fill=Y)
    # Configure the canvas
    cnava.configure(yscrollcommand=my_scrollbar.set)
    cnava.bind("<Configure>", lambda e: cnava.configure(scrollregion=cnava.bbox("all")))
    # Create another frame inside the canvas
    second_frame = Frame(cnava)
    # Add that new frame to a window in the canvas
    cnava.create_window((0, 0), window=second_frame, anchor="nw")
    # Creating a loop to grid 100 buttons
    for i in range(100):
        ttk.Button(second_frame, text=f"This is the {i}th button").grid(row=i, column=0)


btn = Button(download_fr, text="Pack progress bars. ", command=pack_bars)
# Create a canvas
cnava = Canvas(download_fr, bg="red")
# Create a Scrollbar
my_scrollbar = ttk.Scrollbar(download_fr, orient=VERTICAL, command=cnava.yview)
big_lbl.pack(pady=(85, 5))
prg_br.pack()
prg_br.start(5)
cnava.pack(side=LEFT, fill=BOTH, expand=1)
btn.pack()
download_fr.pack(fill=BOTH, expand=1)
root.mainloop()

似乎在第一个程序中,画布也没有调整大小,因为红色区域没有改变,因此按钮彼此重叠。我该如何解决这个问题?

【问题讨论】:

    标签: python tkinter widget tkinter-canvas


    【解决方案1】:

    您创建一个新的second_frame,并在每次执行pack_bars() 时将它放在画布中的相同位置。您应该在函数外部创建second_frame

    您还应该将&lt;Configure&gt; 绑定到second_frame 而不是cnava

    以下是根据你的修改后的代码:

    from tkinter import *
    from tkinter import ttk
    
    available_row = 0
    
    root = Tk()
    root.title("A Scrollbar")
    root.geometry("580x308")
    # Create a download frame
    download_fr = Frame(root, width=580, height=308)
    big_lbl = Label(download_fr, text="Just a BIG label. ", font=("Helvetica", "30", "bold"))
    prg_br = ttk.Progressbar(download_fr, orient=HORIZONTAL, length=300, mode='indeterminate')
    
    
    
    def pack_bars():
        global available_row
        big_lbl.pack_forget()
        prg_br.pack_forget()
        # Pack the Scrollbar
        my_scrollbar.pack(side=RIGHT, fill=Y)
        # add a new button to second_frame
        ttk.Button(second_frame, text=f"This is the {available_row}th button").grid(row=available_row, column=0)
        available_row += 1
        print(f"Next available row: {available_row}")
    
    
    btn = Button(download_fr, text="Pack progress bars. ", command=pack_bars)
    # Create a canvas
    cnava = Canvas(download_fr, bg="red")
    # Create a Scrollbar
    my_scrollbar = ttk.Scrollbar(download_fr, orient=VERTICAL, command=cnava.yview)
    cnava.configure(yscrollcommand=my_scrollbar.set)
    
    big_lbl.pack(pady=(85, 5))
    prg_br.pack()
    prg_br.start(5)
    cnava.pack(side=LEFT, fill=BOTH, expand=1)
    btn.pack()
    download_fr.pack(fill=BOTH, expand=1)
    
    # Create another frame inside the canvas
    second_frame = Frame(cnava)
    # Add that new frame to a window in the canvas
    cnava.create_window(0, 0, window=second_frame, anchor='nw')
    # update canvas scrollregion whenever the size of second_frame is changed
    second_frame.bind('<Configure>', lambda e: cnava.configure(scrollregion=cnava.bbox('all')))
    
    root.mainloop()
    

    【讨论】:

    • 感谢您的解释!你是对的,我应该在函数之外定义框架。为什么我自己没有想到!你太棒了。
    最近更新 更多