Tkinter:访问使用 for 循环创建的特定小部件

Tkinter: access specifc widgets created with for loop

在我的 tkinter 项目中,我使用 for 循环为行列表中的每一行创建了一个条目、一个标签和两个按钮。我还在创建时将它们保存在列表中。

现在我的问题是如何访问它们?例如:如果单击第 12 行的编辑按钮,那么我希望能够获取第 12 个条目的条目值,或者如果我的用户单击列表中第 3 行的删除按钮,则只有条目, 选中行的标签和两个按钮应该被删除。

这是我的代码:

self.line_loop = []

for line in self.line_list:
    self.row_count += 1

    self.n_entry = tk.Entry(self.list_frame, justify="center", width=4)
    self.n_entry.grid(row=self.row_count, column=0, pady=10, padx=10, ipady=3)

    self.text_label = tk.Label(self.list_frame, text=line, anchor="w", justify="left", 
                               wraplengt=701)
    self.text_label.grid(row=self.row_count, column=1, pady=10, padx=10, sticky="w")

    self.edit_button = tk.Button(self.list_frame, text="Edit", command=self.edit)
    self.edit_button.grid(row=self.row_count, column=2, pady=10, padx=10)

    self.delete_button = tk.Button(self.list_frame, text="Delete", command=self.edit)
    self.delete_button.grid(row=self.row_count, column=3, pady=10, padx=10)

    self.line_loop.append(self.n_entry)
    self.line_loop.append(self.text_label)
    self.line_loop.append(self.edit_button)
    self.line_loop.append(self.delete_button)

编辑: 这是函数的示例。该代码应仅适用于单击的按钮和链接的小部件

def delete(self):
    self.n_entry.destroy() 
    self.text_label.destroy() 
    self.edit_button.destroy() 
    self.delete_button.destroy() 
def edit(self): 
    for entry in self.line_loop:
       print(entry.get())

我该怎么做?

您没有提供有关编辑或删除功能的任何详细信息,但我猜想如果您获得相应的按钮来为该功能提供索引,该功能可能会索引到 self.line_list

        self.line_loop = []

        for index, line in enumerate(self.line_list):
            self.row_count += 1

            n_entry = tk.Entry(self.list_frame, justify="center", width=4)
            n_entry.grid(row=self.row_count, column=0, pady=10, padx=10, ipady=3)

            text_label = tk.Label(self.list_frame, text=line, anchor="w", justify="left", wraplengt=701)
            text_label.grid(row=self.row_count, column=1, pady=10, padx=10, sticky="w")

            edit_button = tk.Button(self.list_frame, text="Edit", command=lambda x=index: self.edit(x))
            edit_button.grid(row=self.row_count, column=2, pady=10, padx=10)

            delete_button = tk.Button(self.list_frame, text="Delete", command=lambda x=index: self.delete(x))
            delete_button.grid(row=self.row_count, column=3, pady=10, padx=10)

            self.line_loop.append((n_entry, text_label, edit_button, delete_button))
    def edit(self, index):
        n_entry, text_label, edit_button, delete_button = self.line_loop[index]
        line = self.line_list[index]
        ...

我使用 enumerate()self.line_list 提供索引,并使用 command=lambda 习惯用法为示例 edit() 方法提供索引参数。

你的问题在细节上有点粗略,但这里是如何做你想做的。它利用了您正在使用 tkinter grid 几何管理器这一事实,该管理器跟踪其控制下的所有小部件的行和列位置。这意味着您可以从中获取所需的信息,而不必自己跟踪它。即不需要 self.line_loop 列表和您手动创建的许多其他实例属性(尽管一般来说,如果做得好,这也是一种可行的方法)。

在循环中创建小部件时,务必确保传递给回调函数的任何变量的 value 不在每次循环迭代都会更改的变量中,因为它不起作用(参见 tkinter creating buttons in for loop passing command arguments)。

避免该问题的一种常见方法是使用 lambda 函数,其参数已被赋予默认值,作为“捕获”循环变量当前值的一种方式。在这种情况下,它还需要分两步设置 Button 小部件的选项,以便将按钮本身传递给关联的 command= 回调函数。

这是一个基于您提出的问题中的代码的可运行示例:

import tkinter as tk

class ListEditor:
    def __init__(self, list_frame, lines):
        self.list_frame = list_frame
        self.line_list = lines.splitlines()
        self.create_widgets()

    def create_widgets(self):
        for row, line in enumerate(self.line_list):
            entry = tk.Entry(self.list_frame, justify="center", width=4)
            entry.grid(row=row, column=0, pady=10, padx=10, ipady=3)

            text_label = tk.Label(self.list_frame, text=line, anchor="w",
                                  justify="left", wraplength=701)
            text_label.grid(row=row, column=1, pady=10, padx=10, sticky="w")

            edit_button = tk.Button(self.list_frame, text="Edit")
            edit_button.config(command=lambda widget=edit_button: self.edit(widget))
            edit_button.grid(row=row, column=2, pady=10, padx=10)

            delete_button = tk.Button(self.list_frame, text="Delete")
            delete_button.config(command=lambda widget=delete_button: self.delete(widget))
            delete_button.grid(row=row, column=3, pady=10, padx=10)

    def _get_widgets_on_same_row(self, widget):
        """Return list of all widgets on same row as given one in column order."""
        row = widget.grid_info()['row']
        return sorted(widget.master.grid_slaves(row=row),
                      key=lambda w: w.grid_info()['column'])

    def delete(self, widget):
        row = widget.grid_info()['row']
        widgets = self._get_widgets_on_same_row(widget)
        for widget in widgets:  # Get rid of associated widgets.
            widget.grid_remove()
        self.line_list[row] = None  # Indicate line was deleted.

    def edit(self, widget):
        row = widget.grid_info()['row']
        entry,text_label,edit_button,delete_button = self._get_widgets_on_same_row(widget)
        pass  # Whatever you want to do with the linked widgets (and instance data)...


if __name__ == '__main__':
    from textwrap import dedent

    lines = dedent('''\
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        Vivamus et leo id felis faucibus varius quis et risus.
        Ut nec felis ut enim tincidunt maximus.
        Sed at nunc eleifend, vestibulum dolor nec, dictum tellus.
        Aliquam et lorem tincidunt, rhoncus libero sed, molestie massa.
        Duis quis nunc maximus, semper justo placerat, posuere turpis.
    ''')

    root = tk.Tk()
    list_frame = tk.Frame(root)
    list_frame.pack()
    editor = ListEditor(list_frame, lines)
    root.mainloop()

您必须使用 name 为小部件创建一个名称,我们以后可以通过该名称访问它。

您可以使用 root.nametowidget 通过在创建小部件时指定的名称获取小部件的引用。

编辑:

在索引的帮助下,您可以跟踪在单击特定按钮时必须删除哪些按钮和条目。

from tkinter import *

root=Tk()
for i in range(4):
    Entry(name=f'entry{str(i)}').pack()
    Button(text='get', command=lambda temp=i:edit(temp),name=f'e_button{str(i)}').pack()
    Button(text='delete', command=lambda temp=i:delete(temp),name=f'd_button{str(i)}').pack()
def edit(num):
    Label(text=root.nametowidget(f'.entry{num}').get()).pack()
def delete(num):
    root.nametowidget(f'entry{num}').destroy()
    root.nametowidget(f'.e_button{num}').destroy()
    root.nametowidget(f'.d_button{num}').destroy()
    print('deleted',num)
root.mainloop()