如何删除选择的自动生成的条目框

How to delete chosen auto-generated entry box

编辑以反映 Paul Cornelius 的建议:

我在尝试删除自动生成的输入框时遇到困难:

每个 x_input 的两边都有相应的 v_inputv_input 是可选的,我希望相应的复选框能够忘记或停用其配对的 x_input

我一直在尝试使用索引方法,但没有成功。

下面的简化代码,跳到问题代码,查看实际的 generation/destruction 代码。

######### FRAME SET-UP ##########

from tkinter import *

"""Main app frame"""
class build_app(Tk):
    def __init__(self):
        Tk.__init__(self)
        self._frame = None
        self.switch_frame(home)

    """Old frame destruction"""
    def switch_frame(self, frame_class):
        new_frame = frame_class(self)
        if self._frame is not None:
            self._frame.forget()
        self._frame = new_frame
        self._frame.pack()

"""Home frame"""
class home(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)

        """Label frame definitions"""
        build_labelframe = LabelFrame(self, text="Build", padx=5, pady=5)

        """Label frame positions"""
        build_labelframe.grid(row=2, column=0, sticky=N, padx=5, pady=5)

        """Build label definitions"""
        x_label = Label(build_labelframe, text="x")

        """Build label positions"""
        x_label.grid(row=5, column=0)

        """Build entry definition"""
        count_input = Entry(build_labelframe, width=20)

        """Build entry text definition"""
        count_input.insert(0, "Enter count")
        count_input.configure(justify=CENTER)

        """Build entry position"""
        count_input.grid(row=3, column=0, padx=2)

        """create table button command"""
        def click_create_table():
            global given_count

            given_count = int(count_input.get())

########## PROBLEM CODE BEGINS ##########

            """Build entry definition/position function"""
            p = 0

            x = []
            v = []

            remove_v = []

            for i in range(given_count):
                v_input = Entry(build_labelframe, width=20)
                v_input.grid(row=6 + p + i, column=0)
                v_input.configure(justify=CENTER)
                v.append(v_input)

                x_input = Entry(build_labelframe, width=20)
                x_input.grid(row=7 + p + i, column=0)
                x_input.configure(justify=CENTER)
                x.append(x_input)

                def handle_check(n):
                    def remove_v_command():
                        v[n].grid_forget()
                    return remove_v_command

                remove_v_tick = Checkbutton(build_labelframe, text="Remove veil", bd=0, command=lambda:handle_check(remove_v.index(remove_v[i])))
                remove_v_tick.grid(row=6 + p + i, column=1)
                remove_v.append(remove_v_tick)

                p += 2

            remove_v_tick = Checkbutton(build_labelframe, text="Remove veil", bd=0, command=lambda:handle_check(len(remove_v)))
            remove_v_tick.grid(row=6 + p + i, column=1)
            remove_v.append(remove_v_tick)

            v_input = Entry(build_labelframe, width=20)
            v_input.grid(row=6 + p + i, column=0)
            v.append(v_input)

            i += 1

        """Build button definition"""
        create_table_button = Button(build_labelframe, text="Create table", width=15, command=click_create_table)

        """Build button position"""
        create_table_button.grid(row=4, column=1)

if __name__ == "__main__":
    app = build_app()
    app.mainloop()

此方法成功地为列出的复选框获取了正确的索引值。但是,它没有正确地将索引值传递给 grid_forget 函数,相应的 v_input 框仍然存在。

这是此类程序的常见问题。问题围绕着你的函数remove_v_command。如果你可以将一个整数变量传递给该函数,指示要从网格中删除哪些小部件,如下所示(我已经创建了 v、x 和 y 成员变量):

def remove_v_command(self, n):
    self.v[n].grid_forget()
    self.x[n].grid_forget()
    self.y[n].grid_forget()

这将协调 v、x 和 y 数组中的小部件,以便将小部件一起删除。我认为这大约就是你想要做的。如果我误解了您的要求,您必须在此处进行一些更改。

但是事件处理器是tkinter内部调用的,没有办法让tkinter传递额外的变量n。有几种方法可以解决这个问题。我更喜欢的一种是使用 Python 闭包,像这样:

def handle_check(self, n):
    def remove_v_command():
        self.v[n].grid_forget()
        self.x[n].grid_forget()
        self.y[n].grid_forget()
    return remove_v_command

然后您还要更改创建 Checkbutton 的代码行,如下所示:

remove_v_tick = Checkbutton(
    build_labelframe, text="Remove veil", bd=0,                           
    command=self.handle_check(len(remove_v)))

方法 handle_check 本身不是事件处理程序。它 returns 一个作为事件处理程序的函数,并且(根据 tkinter 的要求)必须采用零参数。由于每次调用 handle_check 时都会创建一个新的函数对象,因此每个 Checkbox 都有不同的事件处理程序。这些对象记住最初传递的 self 和 n 的值;因此每个复选框都链接到最近添加的条目字段。因此,您现在拥有了知道要从网格中删除哪些对象的事件处理程序。

我认为如果你使 handle_check 成为一个成员函数,并使 x、y 和 v 成员对象成为最干净的。

编辑:这是一个完整的工作程序。

######### FRAME SET-UP ##########

from tkinter import *

"""Main app frame"""
class build_app(Tk):
    def __init__(self):
        Tk.__init__(self)
        self._frame = None
        self.switch_frame(home)

    """Old frame destruction"""
    def switch_frame(self, frame_class):
        new_frame = frame_class(self)
        if self._frame is not None:
            self._frame.forget()
        self._frame = new_frame
        self._frame.pack()

"""Home frame"""
class home(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)

        """Label frame definitions"""
        build_labelframe = LabelFrame(self, text="Build", padx=5, pady=5)

        """Label frame positions"""
        build_labelframe.grid(row=2, column=0, sticky=N, padx=5, pady=5)

        """Build label definitions"""
        x_label = Label(build_labelframe, text="x")

        """Build label positions"""
        x_label.grid(row=5, column=0)

        """Build entry definition"""
        count_input = Entry(build_labelframe, width=20)

        """Build entry text definition"""
        count_input.insert(0, "Enter count")
        count_input.configure(justify=CENTER)

        """Build entry position"""
        count_input.grid(row=3, column=0, padx=2)

        """create table button command"""
        def click_create_table():
            global given_count

            given_count = int(count_input.get())

########## PROBLEM CODE BEGINS ##########

            """Build entry definition/position function"""
            p = 0

            self.x = []
            self.v = []

            remove_v = []

            for i in range(given_count):
                v_input = Entry(build_labelframe, width=20)
                v_input.grid(row=6 + p + i, column=0)
                v_input.configure(justify=CENTER)
                self.v.append(v_input)

                x_input = Entry(build_labelframe, width=20)
                x_input.grid(row=7 + p + i, column=0)
                x_input.configure(justify=CENTER)
                self.x.append(x_input)

                remove_v_tick = Checkbutton(
                    build_labelframe,
                    text="Remove veil",
                    bd=0,
                    command=self.handle_check(len(remove_v)))
                remove_v_tick.grid(row=6 + p + i, column=1)
                remove_v.append(remove_v_tick)

                p += 2

            remove_v_tick = Checkbutton(
                build_labelframe, text="Remove veil", bd=0,
                command=self.handle_check(len(remove_v)))
            remove_v_tick.grid(row=6 + p + i, column=1)
            remove_v.append(remove_v_tick)

            v_input = Entry(build_labelframe, width=20)
            v_input.grid(row=6 + p + i, column=0)
            self.v.append(v_input)

            i += 1

        """Build button definition"""
        create_table_button = Button(build_labelframe,
                                     text="Create table",
                                     width=15, command=click_create_table)

        """Build button position"""
        create_table_button.grid(row=4, column=1)

    def handle_check(self, n):
        def remove_v_command():
            self.v[n].grid_forget()
        return remove_v_command

app = build_app()
app.mainloop()
         

解释:

对 Button 构造函数的调用采用 command=f 形式的关键字参数。这里 'f' 是一个函数对象。典型用法类似于 command=click_create_table,其中“click_create_table”是函数的名称,在某处使用 def click_create_table(...) 语句定义。它也可能类似于 lambda n: handle_check(n) 因为“lambda”创建了一个匿名函数对象。

但不必完全是那样。任何计算为函数对象的表达式都可以使用。这样的表达式是 self.handle_check(len(remove_v)) 因为 self.handle_check returns 一个 函数 。在 Python 中,函数就像其他任何东西一样只是对象;它们可以被返回、存储在变量中、作为参数传递等。

Tkinter 在点击按钮时调用该函数。请注意,tkinter 没有调用 self.handle_check;它正在调用 self.handle_check.

创建并返回的对象

传递给 self.handle_check 的参数用作列表 self.v 的索引。 self.handle_check 返回的函数是“一个对象,当用户单击特定按钮时,它知道 grid_forget 进入哪个 Entry 字段。”它通过跟踪最初作为参数传递给 handle_check.

的值来做到这一点

正好len(remove_v)是要删除的Entry的右索引。那是因为调用 handle_check 时,新的 Checkbutton 尚未添加到 remove_v。第一次循环时,len(remove_v) 将为 0,而 self.v[0] 是单击 Checkbutton 时要隐藏的条目。第二次循环,len(remove_v) 为 1,self.v[1] 是关联的条目。等等。