tkinter simpledialog.askstring结束后光标未返回主窗口Entry组件求助
Tkinter: 关闭simpledialog后让Entry组件自动获得焦点
我正在开发一款文字冒险游戏,启动时会弹出两个simpledialog.askstring对话框,分别询问玩家是否玩过游戏(用于跳过 intro)、是否使用关键词。但完成对话框操作后,主窗口有时会被命令行窗口遮挡,且光标焦点不在主窗口的Entry组件上,要么落在命令行,要么处于无焦点状态。我需要关闭对话框后,光标立即回到Entry组件。
可复现代码:
import tkinter as tk from tkinter import simpledialog from tkinter import scrolledtext class Game: def __init__(self, root): self.root = root self.root.title("Game Window") self.entry_widget = tk.Entry(root) self.entry_widget.pack() self.scrollbox = scrolledtext.ScrolledText(root, width=40, height=10) self.scrollbox.pack() self.on_open() def on_open(self): response = simpledialog.askstring("Question", "Have you played before?") if response is not None: if response.lower() == "no": self.scrollbox.insert(tk.END, "Welcome!\n") elif response.lower() == "yes": self.scrollbox.insert(tk.END, "Let's play!\n") root = tk.Tk() app = Game(root) root.mainloop()
我试过直接调用self.entry_widget.focus_set()、self.root.focus_set(),甚至组合self.entry_widget.focus_set() + self.root.lift() + self.root.focus_force(),但都没效果。
解决方案
问题出在模态对话框关闭后,系统可能优先把焦点还给了终端窗口;同时直接在对话框关闭后调用焦点方法,可能因为Tkinter的事件循环还没处理完对话框的销毁事件,导致焦点设置被覆盖。
可以通过延迟执行焦点设置的方式解决,利用Tkinter的after_idle()方法,让焦点操作在当前所有事件处理完成后再执行:
修改on_open方法如下:
def on_open(self): response = simpledialog.askstring("Question", "Have you played before?") if response is not None: if response.lower() == "no": self.scrollbox.insert(tk.END, "Welcome!\n") elif response.lower() == "yes": self.scrollbox.insert(tk.END, "Let's play!\n") # 延迟到事件队列清空后执行焦点恢复 def restore_focus(): self.root.lift() # 把主窗口提到最上层 self.root.focus_force() # 强制主窗口获得焦点 self.entry_widget.focus_set() # 让Entry组件获得光标 self.root.after_idle(restore_focus)
原理
after_idle()会在Tkinter处理完当前所有待执行的事件(包括对话框的销毁)后,再调用指定函数。这时候系统的焦点状态已经稳定,设置主窗口和Entry的焦点就不会被后续事件覆盖。
如果是在命令行环境下运行,还可以在__init__里提前给主窗口加一次初始焦点,进一步避免焦点丢失:
def __init__(self, root): self.root = root self.root.title("Game Window") self.entry_widget = tk.Entry(root) self.entry_widget.pack() self.scrollbox = scrolledtext.ScrolledText(root, width=40, height=10) self.scrollbox.pack() # 初始启动后让Entry获得焦点 self.root.after(100, lambda: self.entry_widget.focus_set()) self.on_open()
内容的提问来源于stack exchange,提问作者BootzenKatzen
相关产品推荐
相关产品推荐

