如何将焦点设置到 Frame 小部件的未知子项

How can I set focus to an unknown child of the Frame widget

有一个包含多个标签和文本小部件的框架。这些是从 JSON 文档动态生成的。

我想要的是将焦点设置在框架中的第一个文本小部件上。该文本小部件的名称取决于 JSON 文档,所以我不能只使用 Frame.children['name'].

来获取它

这是一个可以完成工作的函数。但我想知道是否有更方便的方法。我考虑过在键盘遍历层次结构中获取第一个小部件,但找不到任何示例。请提出任何建议。谢谢

def focus_to_child(event):
    for k, v in event.widget.children.items():
        if type(v) is Text:
            break
    try:
        event.widget.children[k].focus()
    except UnboundLocalError:
        pass

最小的可重现示例。 下面的示例中的小部件比我在我的项目中少得多。因此,在键盘遍历层次结构中,第一个 Text 小部件是 Treeview 之后的下一个。不要让这个事实让你感到困惑 =)

from random import randint as ri
from tkinter import Tk, ttk, Text

# here's example struct and values, in project MAP is initialized from JSON documents
MAP = {'_a_A': {'_id': '_a_A', 'name': chr(ri(97, 122)), 'quit_prob': 0.1, 'changed': False},
       '_a_B': {'_id': '_a_B', 'name': chr(ri(97, 122)), 'quit_prob': 0.25, 'changed': False},
       '_x_Z': {'_id': '_x_Z', 'name': chr(ri(97, 122)), 'quit_prob': 0.75, 'changed': True}}

# Treeview items selection event handle
def tv_sel_handler(event):
    try:
        # item id of the document that user has selected
        sel_iid = event.widget.selection()[0]
    except IndexError:
        sel_iid = ''
    try:
        generate_entries(MAP[sel_iid])
        # here we should set focus to the first Text in the data_entries Frame
        focus_to_child()
    except KeyError:
        pass

# this is my option at the moment, but I doubt that it's the most convenient method
def focus_to_child():
    for k, v in data_entries.children.items():
        if type(v) is Text:
            break
    try:
        data_entries.children[k].focus()
    except UnboundLocalError:
        pass

# generate Entries for selected document fields
def generate_entries(document):
    
    def focus_widget(event):
        if event.state == 8:
            event.widget.tk_focusNext().focus()
        elif event.state == 9:
            event.widget.tk_focusPrev().focus()
        return 'break'
    
    row = 0
    for field in document.keys():
        # label
        ttk.Label(data_entries, text=field).grid(row=row, column=0, sticky='nw')
        # widget to edit document fields 
        tf = Text(data_entries, name=field, wrap='word', width=20, height=2)
        tf.insert('1.0', document[field])
        tf.grid(row=row+1, sticky='nwe', padx=0, pady=5)
        tf.bind('<Tab>', focus_widget)
        tf.bind('<Shift-Tab>', focus_widget)
        tf.bind('<FocusIn>', lambda event: event.widget.tag_add('sel', '1.0', 'end'))
        # separator
        ttk.Label(data_entries).grid(row=row+2)
        row += 3

root = Tk()
# list of documents
data_docs = ttk.Treeview(root, selectmode='browse', show=('tree',))
data_docs.column('#0', width=220)
data_docs.grid(row=0, column=0, sticky='nsw')
data_docs.bind('<<TreeviewSelect>>', tv_sel_handler)
for doc in MAP.values():
    data_docs.insert('', 'end', doc['_id'], text=doc['_id'][3:])

# frame for dynamic entries
data_entries = ttk.Frame(root, width=1000, height=450, padding=(15,0))
data_entries.grid(row=0, column=1, sticky='nsew')

root.mainloop()

我不知道方便,但更直接、更有效的方法是简单地记住创建第一个文本小部件时的内容 —这很容易做到(见下面的 ALL CAPS COMMENTS):

# generate Entries for selected document fields
def generate_entries(document):

    def focus_widget(event):
        if event.state == 8:
            event.widget.tk_focusNext().focus()
        elif event.state == 9:
            event.widget.tk_focusPrev().focus()
        return 'break'

    row = 0
    for field in document.keys():
        # label
        ttk.Label(data_entries, text=field).grid(row=row, column=0, sticky='nw')
        # widget to edit document fields
        tf = Text(data_entries, name=field, wrap='word', width=20, height=2)
        if row == 0:                      # FIRST TEXT?
            data_entries.first_text = tf  # REMEMBER IT.
        tf.insert('1.0', document[field])
        tf.grid(row=row+1, sticky='nwe', padx=0, pady=5)
        tf.bind('<Tab>', focus_widget)
        tf.bind('<Shift-Tab>', focus_widget)
        tf.bind('<FocusIn>', lambda event: event.widget.tag_add('sel', '1.0', 'end'))
        # separator
        ttk.Label(data_entries).grid(row=row+2)
        row += 3

然后将不再需要通过 for 循环搜索它:

def focus_to_child():
    try:
        data_entries.first_text.focus()
    except AttributeError:
        pass