【问题标题】:How do I fill the rest of my screen design with blank frames in tkinter using for loop?如何使用 for 循环在 tkinter 中用空白帧填充屏幕设计的其余部分?
【发布时间】:2021-02-22 09:39:08
【问题描述】:

我正在尝试制作一个以购物为灵感的自动售货机 UI。目标是使用 for 循环和 2D 列表用框架填充我的框架,然后将按钮和标签打包到上面。代码如下:

    frames = []
    frame_order = []
    num = 0

    for x in range(5):
        frames.append([])
        for y in range(5):
            frames[x].append(5)

    for x in range(5):
        for y in range(5):
            frames[x][y] = Frame(store_canvas, width=1520 / 5, height=1030 / 5, bd = 2, relief = SOLID)
            frames[x][y].grid(row=y, column=x)
            frames[x][y].pack_propagate(False)
            frame_order.append(frames[x][y])

这将重新运行一个 SQL 查询数据库,该数据库将返回一个作为 list[] 的项目列表。然后填充 2D list 内的框架

retrieve_tiem() 

我还在有其他过滤器的一侧设置了另一个框架。问题是,如果过滤器查询 SQL 并返回小于网格大小(5x5,总共 25 个项目)的item list,则for 循环将不会运行,因为它无法填充。为了避免这种情况,我尝试使用 TryExcept 但循环只会用按钮填充剩余空间。

for frame in frame_order:
    try:
        Button(frame, anchor='nw', height = 9, width = 35, font = 20).pack()
        Label(frame, text=str(Item_list[num]), anchor='nw', font = 20, width = 35, bg = 'darkgreen', fg = 'yellow' ).pack()
        num += 1
    except:
        pass

有没有办法避免这种情况?就像在项目列表用完项目时不创建按钮或用'*blank*' 填充列表的其余部分直到列表达到框架可以容纳的项目总数?如果可行,我也对其他方法持开放态度。感谢所有答案,请放轻松,因为我还在学习 python 和 SQL :)

【问题讨论】:

    标签: python sqlite tkinter


    【解决方案1】:

    您实际上可以使用一维列表而不是二维列表。以下是基于您的代码的示例:

    from tkinter import *
    import random
    
    root = Tk()
    
    store_canvas = Frame(root)
    store_canvas.pack()
    
    # create the 25 frames
    ROWS = COLS = 5
    MAX_ITEMS = ROWS * COLS
    frames = []
    for i in range(MAX_ITEMS):
        frames.append(Frame(store_canvas, width=1520/COLS, height=1030/ROWS, bd=2, relief=SOLID))
        frames[-1].grid(row=i//COLS, column=i%COLS)
        frames[-1].pack_propagate(False)
    
    # function to simulate retrieving data from database table
    def retrieve_tiem():
        return [f"Item #{i+1}" for i in range(random.randint(1,MAX_ITEMS))]
    
    # function to show the retrieved items
    def update_list():
        Item_list = retrieve_tiem()
        label_font = ("Arial", 20)
        for i, frame in enumerate(frames):
            for w in frame.winfo_children():
                w.destroy()
            if i < len(Item_list):
                item = Item_list[i]
                Button(frame).pack(fill="both", expand=1)
                Label(frame, text=item, font=label_font, bg="darkgreen", fg="yellow").pack(fill="x")
    
    update_list()
    root.bind("<F5>", lambda e: update_list())
    root.mainloop()
    

    【讨论】:

    • 非常感谢您的回答,我在单独的文件中运行了代码,效果正是我想要的,但是,我很难理解代码的某些部分以及它的实际作用所以如果可能的话,你能简单地向我解释一下吗?我希望我不要问太多:1.对于frame.grid,为什么[-1]是-1? 2. {} 是什么意思? 3.enumerate是什么意思,有什么作用?
    • @Y0SHI0N 1) frames[-1] 表示列表中的最后一项frames; 2) f"{...}" 将计算{} 中的表达式并将结果放入f-string; 3) 见enumerate函数官方文档。
    • 更新:自上一条评论以来;我试图复制并粘贴代码,并调整变量,它就像一个魅力......非常感谢......我仍然不知道为什么它可以工作虽然 lul :) 编辑:哎呀没有看到你的cmets,谢谢你的解释,我去看看链接
    【解决方案2】:

    问题在于您的try 语句。这是一个例子:

    a = 5
    try:
        a += 7
        int("str") #This is just to raise an exception
    except:
        pass
    

    运行此代码后,a 的值将是 12,即使发生错误。这与您的代码中发生的事情相同。创建按钮的行运行成功,但创建标签引发异常。这就是您在剩余空间中获得按钮的原因。这可以使用else 解决。 如果我们尝试这样做:

    a = 5
    try:
        int("str") #This is just to raise an exception
    except:
        pass
    else:
        a += 7
    

    a += 7 行只有在try 语句中没有异常时才会运行,因此该值将保持为 5。对于您的代码,这将是

    try:
        Item_list[num] #This is what causes the error
    except:
        pass
    else:
        Button(frame, anchor='nw', height = 9, width = 35, font = 20).pack()
        Label(frame, text=str(Item_list[num]), anchor='nw', font = 20, width = 35, bg = 'darkgreen', fg = 'yellow' ).pack()
        num += 1
    

    或者,您可以使用if 语句来检查num 是否大于返回的数据的长度,但问题中没有足够的信息让我确定它会起作用。

    【讨论】:

    • 感谢您的回复。我明白为什么现在会发生这种情况。
    猜你喜欢
    • 2021-04-05
    • 2011-12-10
    • 2019-04-01
    • 2015-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-24
    相关资源
    最近更新 更多