python-3.x - 不同的焦点顺序和堆叠顺序 tkinter

标签 python-3.x tkinter focus tkinter-text

在下面的示例中,我创建了多个文本条目,其中一些文本条目在您聚焦时可以展开/折叠。

但是,当某些扩展(具体来说,名为 entry_2_problem_1entry_5_problem_2 的扩展,我还在“此处”插入了文本)时,它们会在另一个文本“下方”结束条目 - 我的意思是,由于堆叠顺序,它们位于另一个条目下方。

在创建向上堆叠条目(即 entry_3entry_6,但这些会改变我的焦点顺序。我想要一个“自然”的焦点顺序,从左到右,从上到下。

下面,您可以看到带有一些注释行的代码:如果您取消注释这些行,您会发现堆叠问题被焦点顺序问题所取代(因为它并不是真正从左到右,正如您在使用时会注意到的那样)选项卡)。

此外,考虑到在我正在处理的实际代码中,由于多种原因,在小部件之间留下更多空白的任何内容都会被丢弃

MRE:

from tkinter import Tk, Text

def focus_next_widget(event):
    event.widget.tk_focusNext().focus()
    return("break")

class iText(Text):
    def __init__(self, stdwidth_mult=2.5, stdheight_mult=3, **kwargs):
        super(iText, self).__init__(**kwargs)

        self.stdwidth = kwargs.get('width')
        self.stdheight = kwargs.get('height')
        self.stdwidth_mult = stdwidth_mult
        self.stdheight_mult = stdheight_mult

def text_resizer(event):
    if event.widget == event.widget.focus_get():
        if not event.widget.stdheight == None:event.widget.configure(height=int(event.widget.stdheight*event.widget.stdheight_mult))
        if not event.widget.stdwidth == None: event.widget.configure(width=int(event.widget.stdwidth*event.widget.stdwidth_mult))
    else:
        if not event.widget.stdheight == None:event.widget.configure(height=event.widget.stdheight)
        if not event.widget.stdwidth == None: event.widget.configure(width=event.widget.stdwidth)

window = Tk()
window.geometry("300x300")

entry1 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry1.place(x=6.0, y=68.0, height=43.0)
entry1.bind("<Tab>", focus_next_widget)
entry1.bind('<FocusIn>', text_resizer)
entry1.bind('<FocusOut>', text_resizer)

# First problematic entry
entry_2_problem_1 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_2_problem_1.place(x=6.0, y=116.0, height=43.0)
entry_2_problem_1.insert(1.0, 'Here')
entry_2_problem_1.bind("<Tab>", focus_next_widget)
entry_2_problem_1.bind('<FocusIn>', text_resizer)
entry_2_problem_1.bind('<FocusOut>', text_resizer)

entry_3 = iText(stdheight_mult=1, height=1, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_3.place(x=70.0, y=121.0, width=102.0)
entry_3.bind("<Tab>", focus_next_widget)
entry_3.bind('<FocusIn>', text_resizer)
entry_3.bind('<FocusOut>', text_resizer)
# The following line solves the stacking problem, but creates a focus order one
# entry_2_problem_1.lift()

entry_4 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_4.place(x=6.0, y=165.0, height=43.0)
entry_4.bind("<Tab>", focus_next_widget)
entry_4.bind('<FocusIn>', text_resizer)
entry_4.bind('<FocusOut>', text_resizer)

# Second problematic entry
entry_5_problem_2 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_5_problem_2.place(x=6.0, y=213.0, height=43.0)
entry_5_problem_2.insert(1.0, 'Here')
entry_5_problem_2.bind("<Tab>", focus_next_widget)
entry_5_problem_2.bind('<FocusIn>', text_resizer)
entry_5_problem_2.bind('<FocusOut>', text_resizer)

entry_6 = iText(stdheight_mult=1, height=1, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_6.place(x=70.0, y=218.0, width=102.0, height=34.0)
entry_6.bind("<Tab>", focus_next_widget)
entry_6.bind('<FocusIn>', text_resizer)
entry_6.bind('<FocusOut>', text_resizer)
# The following line solves the stacking problem, but creates a focus order one
# entry_8_problem_2.lift()

window.mainloop()

此外,还有一些当前和所需输出的照片,涉及堆叠问题。

Current stacking-situation (with GOOD focus order)

Desired stacking-situation (but has a BAD focus order behavior)

最佳答案

我无法发表评论(由于代表阈值),因此我无法提出澄清问题,但我相信您想要的行为是按 Tab 键前进 iText 条目的每一行按照 tkinter 默认行为,按照您声明的顺序排列框,直到您 lift() 一个对象。

最简单的答案

最简单的方法是在小部件获得焦点时而不是在声明它们时使用.lift(),以便您获得焦点的小部件始终高于其他小部件当你使用它时:

def text_resizer(event):
    if event.widget == event.widget.focus_get():
        event.widget.lift() # Always lift the current widget when it gains focus
        if not event.widget.stdheight == None:event.widget.configure(height=int(event.widget.stdheight*event.widget.stdheight_mult))
        if not event.widget.stdwidth == None: event.widget.configure(width=int(event.widget.stdwidth*event.widget.stdwidth_mult))
    else:
        if not event.widget.stdheight == None:event.widget.configure(height=event.widget.stdheight)
        if not event.widget.stdwidth == None: event.widget.configure(width=event.widget.stdwidth)

在此应用程序中,默认输入框在未展开时没有任何重叠,这是有效的。这还假设您只想使用事件系统循环浏览元素,而不是按特定顺序单击它们。

只要不是这种情况,请尝试以下操作:

更加动态的方法

但是,如果您的布局更紧凑,并且有许多重叠的输入框(实际上是任何小部件),那么当它们的相对层发生变化时,可能会导致它们在视觉上变得困惑。为了避免这种情况,您可以将对输入框对象的引用放入可迭代对象中,并编写一个函数,按照可迭代对象中的顺序对它们进行排序。在此示例中,我搭载了您的 focusOut 回调:

def text_resizer(event):
    if event.widget == event.widget.focus_get():
        event.widget.lift() # Always lift the current widget when it gains focus
        if not event.widget.stdheight == None:event.widget.configure(height=int(event.widget.stdheight*event.widget.stdheight_mult))
        if not event.widget.stdwidth == None: event.widget.configure(width=int(event.widget.stdwidth*event.widget.stdwidth_mult))
    else:
        if not event.widget.stdheight == None:event.widget.configure(height=event.widget.stdheight)
        if not event.widget.stdwidth == None: event.widget.configure(width=event.widget.stdwidth)
        for widget in window.widget_order:
            window.widget_order[widget].lift() # Sort the widgets back into their default order to restore the appearance

将小部件添加到可迭代的位置,如下所示:

window.widget_order[len(window.widget_order)] = entry1 # Add the widget to the widget order list

这将出现与您最初遇到的相同问题,因此跟踪当前聚焦的小部件并更改将焦点从 tkinter 的 focusNext() 切换的方法将很有趣,这可能是您首先选择的解决方案:

window = Tk()
window.geometry("300x300")
window.widget_order = {}    # Store the desired visual ordering of widgets
window.currentFocus = 0     # Store the current focus number to use as a key for the widget dict

def focus_next_widget(event):
    window.currentFocus = (window.currentFocus + 1) % len(window.widget_order) # Calculate the next focus value, wrapping based on dict length
    window.widget_order[window.currentFocus].focus() # Focus the next widget
    return("break")

完整的代码将如下所示:

from tkinter import Tk, Text

def focus_next_widget(event):
    window.currentFocus = (window.currentFocus + 1) % len(window.widget_order) # Calculate the next focus value, wrapping based on dict length
    window.widget_order[window.currentFocus].focus() # Focus the next widget
    return("break")

class iText(Text):
    def __init__(self, stdwidth_mult=2.5, stdheight_mult=3, **kwargs):
        super(iText, self).__init__(**kwargs)

        self.stdwidth = kwargs.get('width')
        self.stdheight = kwargs.get('height')
        self.stdwidth_mult = stdwidth_mult
        self.stdheight_mult = stdheight_mult

def text_resizer(event):
    if event.widget == event.widget.focus_get():
        event.widget.lift() # Always lift the current widget when it gains focus
        if not event.widget.stdheight == None:event.widget.configure(height=int(event.widget.stdheight*event.widget.stdheight_mult))
        if not event.widget.stdwidth == None: event.widget.configure(width=int(event.widget.stdwidth*event.widget.stdwidth_mult))
    else:
        if not event.widget.stdheight == None:event.widget.configure(height=event.widget.stdheight)
        if not event.widget.stdwidth == None: event.widget.configure(width=event.widget.stdwidth)
        for widget in window.widget_order:
            window.widget_order[widget].lift() # Sort the widgets back into their default order to restore the appearance

window = Tk()
window.geometry("300x300")
window.widget_order = {}    # Store the desired visual ordering of widgets
window.currentFocus = 0     # Store the current focus number to use as a key for the widget dict

entry1 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry1.insert(1.0, "e1")
entry1.place(x=6.0, y=68.0, height=43.0)
entry1.bind("<Tab>", focus_next_widget)
entry1.bind('<FocusIn>', text_resizer)
entry1.bind('<FocusOut>', text_resizer)
window.widget_order[len(window.widget_order)] = entry1 # Add the widget to the widget order list

# First problematic entry
entry_2_problem_1 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_2_problem_1.place(x=6.0, y=116.0, height=43.0)
entry_2_problem_1.insert(1.0, "e2")
entry_2_problem_1.bind("<Tab>", focus_next_widget)
entry_2_problem_1.bind('<FocusIn>', text_resizer)
entry_2_problem_1.bind('<FocusOut>', text_resizer)
window.widget_order[len(window.widget_order)] = entry_2_problem_1 # Add the widget to the widget order list

entry_3 = iText(stdheight_mult=1, height=1, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_3.place(x=70.0, y=121.0, width=102.0)
entry_3.insert(1.0, "e3")
entry_3.bind("<Tab>", focus_next_widget)
entry_3.bind('<FocusIn>', text_resizer)
entry_3.bind('<FocusOut>', text_resizer)
window.widget_order[len(window.widget_order)] = entry_3 # Add the widget to the widget order list

entry_4 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_4.place(x=6.0, y=165.0, height=43.0)
entry_4.insert(1.0, "e4")
entry_4.bind("<Tab>", focus_next_widget)
entry_4.bind('<FocusIn>', text_resizer)
entry_4.bind('<FocusOut>', text_resizer)
window.widget_order[len(window.widget_order)] = entry_4 # Add the widget to the widget order list

# Second problematic entry
entry_5_problem_2 = iText(width=4, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_5_problem_2.place(x=6.0, y=213.0, height=43.0)
entry_5_problem_2.insert(1.0, 'e5')
entry_5_problem_2.bind("<Tab>", focus_next_widget)
entry_5_problem_2.bind('<FocusIn>', text_resizer)
entry_5_problem_2.bind('<FocusOut>', text_resizer)
window.widget_order[len(window.widget_order)] = entry_5_problem_2 # Add the widget to the widget order list

entry_6 = iText(stdheight_mult=1, height=1, bd=2, font='futura', relief='flat', highlightcolor='#3A3A3A', highlightbackground='#3A3A3A', highlightthickness=2, bg="#D9D9D9", fg="#000716")
entry_6.place(x=70.0, y=218.0, width=102.0, height=34.0)
entry_6.insert(1.0, 'e6')
entry_6.bind("<Tab>", focus_next_widget)
entry_6.bind('<FocusIn>', text_resizer)
entry_6.bind('<FocusOut>', text_resizer)
window.widget_order[len(window.widget_order)] = entry_6 # Add the widget to the widget order list

window.mainloop()

这还允许您划分所有小部件订单代码,以便更轻松地更改订单;只需将它们以新的顺序插入到字典中即可。对我来说这是有道理的,尽管其他人可能有所不同。

我使用了字典,但我相信列表也同样有效。

关于python-3.x - 不同的焦点顺序和堆叠顺序 tkinter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76687888/

相关文章:

python - 在函数内修改字典

Python参数注解未解析引用

list - Python函数输出错误,检查索引以查看值?

python - 将变量从组合框传递到被调用函数

jQuery focus() 在 FireFox 中不起作用,但在 Chrome 中起作用

javascript - 悬停显示子元素,失去焦点时隐藏它

python - 如何检查列表 'a' 中的元素是否满足列表 'b' 中的条件?

python - 我可以设置禁用 filedialog.askdirectory() tkinter 小部件的 "New Folder"按钮吗?

python - Tkinter create_image() 保留 PNG 透明度,但 Button(image) 不保留

java - 获取 ID,Selenium 中当前关注的元素的类