Python 如何动态更新tkinter小部件的层?

Python 如何动态更新tkinter小部件的层?,python,tkinter,Python,Tkinter,特金特要求 因此,我对使用tkinter比较陌生,我在这里遇到了一个非常具体的疑问。我试图找到解决办法,但我发现很明显,解决办法似乎并不容易理解。因此,如果您可以看到上面的图像,我正在尝试为一个需要多层(我称之为基于3D阵列)小部件的特定项目创建GUI。 假设此指针系统使用的变量是i、j和k 我正在使用for循环创建各个层小部件: for n in range(i): frame_x[i] = Frame(root).grid(row = 1, column = i) entr

特金特要求

因此,我对使用
tkinter
比较陌生,我在这里遇到了一个非常具体的疑问。我试图找到解决办法,但我发现很明显,解决办法似乎并不容易理解。因此,如果您可以看到上面的图像,我正在尝试为一个需要多层(我称之为基于3D阵列)小部件的特定项目创建GUI。 假设此指针系统使用的变量是
i
j
k

我正在使用
for
循环创建各个层小部件:

for n in range(i):
    frame_x[i] = Frame(root).grid(row = 1, column = i)
    entry_x[i] = Entry(frame_x[i]).grid(row = 2, column = i)
    button_x[i] = Button(frame_x[i]).grid(row=3, column = i)
请注意,这不是一个功能代码,我试图保持它的要点,只是为了给我的方法使用的想法。(如果需要更详细的代码块,请告诉我。)

现在来谈谈问题。我能做这个的基本部分。但问题是我希望它能动态工作

假设用户首先输入j=4。将创建4个块。 稍后,如果他将值更改为j=2并按下按钮,理想情况下,应该使块j=3和4处的小部件消失。但是我猜
tkinter
是在重叠的基础上工作的,并且在某个网格元素被明确地重叠之前不会改变它。我该怎么做呢。我尝试在进入
for
循环后销毁整个帧,但这不起作用,因为在销毁之前第一次没有创建小部件,python抛出
namererror
说我不能在赋值之前使用变量

无论如何,请让我知道我如何有效地做到这一点。 总的来说,如果有更好的方法来处理整个事情的话。请参考上面的图片,如果没有意义请告诉我


总的来说,我不太喜欢上课。我更喜欢低效的方式,只使用函数来完成所有我必须做的事情。因此,如果您可以在不使用类的情况下与我共享一个框架,那就太好了。但如果你用它们也没关系。我知道我应该在某个时候开始学习课程。

首先,我想解决这部分问题:

我猜tkinter是在重叠的基础上工作的,在某个网格元素被明确地重叠之前不会改变它

我不完全确定你说的是什么意思,但如果这是我认为的意思,那就是一个错误的说法。tkinter不会“在重叠的基础上工作”。如果您销毁一个小部件,它将被销毁。它是否重叠并不重要


基于您发布的一点点代码,主要问题是您没有将条目和按钮放在框架中。因此,当您破坏框架时,它们不会被破坏

您没有将小部件放入框架的原因是因为这一行:

frame_x[i] = Frame(root).grid(row = 1, column = i)
在python中,如果执行
x=y().z()
x
的值为
z()
。因此,当您执行
frame_x[i]=frame(…).grid(…)
时,
frame_x[i]
的值为
.grid(…)
,并且
.grid(…)
始终返回无。因此,
frame_x[i]
将是
None

下次执行
entry\ux[i]=entry(frame\ux[i]).grid(…)
时,与执行
entry\ux[i]=entry(None).grid(…)
时相同。由于
条目的主项是
None
,因此它成为根窗口的子项

因此,第一步是将小部件的创建与小部件的布局分开

frame_x[i] = Frame(root)
frame_x[i].grid(row = 1, column = i)
一旦您这样做,
条目
按钮
小部件将成为框架的子部件,您可以通过销毁框架(例如:
frame\u x[i].destroy()
)删除您不想要的小部件,因为销毁小部件也会导致小部件的所有子部件被销毁

一旦设置好了,只需在框架上调用
.destroy()
,就可以销毁不需要的小部件。例如,如果您以前创建了10个组,现在只需要5个,则可以销毁其他组,然后将其从列表中删除,如下所示:

# assume 'num' contains the number of frames that we want,
# and that it is smaller than the number of items in frames_x
for frame in frames_x[num:]:
    frame.destroy()
frames_x = frames_x[:num]

这里有一个完整的工作程序来说明。输入一个数字,然后单击按钮。它将创建许多帧+条目+按钮组合。输入一个较大或较小的新数字,它将添加或删除小部件

如果您使用类,这将更容易,但您特别要求提供一个不使用类的解决方案。在实际代码中,您可能还需要将条目小部件保存在一个数组中,以便以后可以引用它们,但本示例主要关注小部件的创建,而不是为您编写整个程序

import tkinter as tk

frames_x = [] def create_widgets():
    global frames_x

    num = int(num_widgets.get())
    # if the number is less than the previous number of
    # widgets, delete the widgets we no longer want
    for frame in frames_x[num:]:
        frame.destroy()
    frames_x = frames_x[:num]

    # if the number is greater than the previous number of
    # widgets, create additional widgets
    for i in range(len(frames_x), num):
        # create new widget
        frame = tk.Frame(root, bd=1, relief="raised")
        entry = tk.Entry(frame)
        button = tk.Button(frame, text="click me")

        # pack entry and button in frame
        button.pack(side="right")
        entry.pack(side="left", fill="x", expand=True)

        # grid the frame in the parent
        frame.grid(row=i+1, column=0, columnspan=2)

        # save the new frame in the array
        frames_x.append(frame)

root = tk.Tk() num_widgets = tk.Entry(root) button = tk.Button(root, text="Create widgets", command=create_widgets)

button.grid(row=0, column=1) num_widgets.grid(row=0, column=0, sticky="ew")

root.mainloop()

您需要提供更多的代码,请参阅。我认为您可以通过为每个您所称的“层”创建单独的页面来处理此问题,这将允许您轻松地从一个切换到另一个以及删除它们。首先,请参见@Bryan Oakley的问题解答,以获取创建它们的示例。关于
tkinter
在循环之前抛出
namererror
,应该比较容易找到允许删除单个页面的方法。如果您无法知道这是否是第一次,请将销毁帧包装在一个
块中,尝试
/
,但
块除外并忽略它。你还需要克服上课时的不适感,因为上课通常是最简单、更好的方式,而且并不难理解。只要把它们想象成可重用的代码“小部件”。@martineau:我谦恭地请求您不要推荐这种解决方案。这从来不是为初学者设计的通用起点。这一答案引发了数百个新问题。我写这篇文章时假设页面是静态的,并且使用该模式的人已经理解了tkinter。@martineau:老实说?我会说艾尔