Python 防止tkinter条目在其关联的StringVar更改时获得焦点

Python 防止tkinter条目在其关联的StringVar更改时获得焦点,python,tkinter,Python,Tkinter,我有一个带有validate命令的tkinter条目,在条目获得焦点(“focusin”)时执行。此条目与StringVar关联。似乎每当StringVar更改值时,条目就会获得焦点,从而触发验证命令。例如: import Tkinter as tk window = tk.Tk() var = tk.StringVar() def validate(*args): print("Validation took place") return True entry = tk.En

我有一个带有validate命令的tkinter条目,在条目获得焦点(“focusin”)时执行。此条目与
StringVar
关联。似乎每当StringVar更改值时,条目就会获得焦点,从而触发验证命令。例如:

import Tkinter as tk

window = tk.Tk()
var = tk.StringVar()
def validate(*args):
    print("Validation took place")
    return True
entry = tk.Entry(validate="focusin", validatecommand=validate)
print("Entry created. Associating textvariable")
entry.config(textvariable=var)
print("textvariable associated. Changing value")
var.set("Text")
print("Value changed")
entry.pack()
tk.mainloop()
此代码生成以下输出:

Entry created. Associating textvariable
textvariable associated. Changing value
Validation took place
Value changed
请注意,由于调用
var.set
,执行了验证命令。
有没有一种方法可以让我更改StringVar的值,而不会导致其关联条目获得焦点?我无法暂时解除StringVar与条目的关联,因为当重新关联它们时,条目也会获得焦点。

一种可能的解决方法是在设置StringVar之前禁用验证事件,然后重新启用它

import Tkinter as tk

window = tk.Tk()
var = tk.StringVar()
def validate(*args):
    print("Validation took place")
    return True
entry = tk.Entry(validate="focusin", validatecommand=validate)
print("Entry created. Associating textvariable")
entry.config(textvariable=var)
print("textvariable associated. Changing value")
entry.config(validate="none")
var.set("Text")
entry.config(validate="focusin")
print("Value changed")
entry.pack()
tk.mainloop()
如果您希望经常这样做,您甚至可以编写一个上下文管理器,这样您就不会意外地忘记在修改完文本后重新启用验证

import Tkinter as tk
from contextlib import contextmanager

@contextmanager
def temp_change(widget, **kargs):
    old_values = {key:widget.cget(key) for key in kargs}
    widget.config(**kargs)
    try:
        yield #wait for `with` block to finish executing...
    finally:
        widget.config(**old_values)


window = tk.Tk()
var = tk.StringVar()
def validate(*args):
    print("Validation took place")
    return True
entry = tk.Entry(validate="focusin", validatecommand=validate)
print("Entry created. Associating textvariable")
entry.config(textvariable=var)
print("textvariable associated. Changing value")

with temp_change(entry, validate="none"):
    var.set("Text")

print("Value changed")
entry.pack()
tk.mainloop()

我认为您的观察是错误的:当您设置
StringVar
的值时,焦点不会改变。这可能只是一种边缘情况,只有在应用程序首次启动时才会发生。小部件可能在最初创建时获得焦点。一旦GUI启动并运行,设置变量不会改变焦点

不鼓励同时使用验证和变量。从文件中:

一般来说,textVariable和validateCommand混合使用可能很危险。任何问题都已克服,因此使用validateCommand不会干扰entry小部件的传统行为


在这种情况下,没有理由使用
StringVar
。我的建议是删除它。通常,只有当您对多个小部件使用同一个变量,或者正在对该变量进行跟踪时,才可以使用
StringVar
。您既不这样做,也不这样做。请停止使用
StringVar
。它添加了一个额外的对象进行管理,但没有提供太多额外的好处。

这就是我现在正在做的,但它感觉非常粗糙。我实际上正在对Entry类进行子类化,子类负责绑定validationcommand,但
var.set(…)
发生在类之外,这一事实加剧了这个问题。我发现“用户”必须知道,不只是简单地更改变量,而不会发生副作用,这很难看。不过,如果很快没有其他消息出现,我会将此标记为已接受。是的,我同意你的看法,这不是一个很好的解决方案。如果是我,我可能会将Entry、StringVar和validationcommand都放在一个类中,并且只通过我自己设计的合理接口公开它们。(我在回答中没有这样做,因为具体的设计取决于扩展条目将满足的具体需求)我发布的只是一个显示行为的小示例。不幸的是,在GUI启动并运行之后,我的实际用例也发现了这种行为。我不知道不鼓励组合变量和验证——我会看看是否可以更改代码,使其不需要。好吧,您可能会看到相同的行为,但这并不是因为焦点实际发生了变化。通过添加第二个条目小部件和设置值的按钮,您可以很容易地看到这一点。将焦点设置为另一个条目,单击按钮,然后查看焦点是否不变。问题不在于焦点在改变,而在于验证的发生与焦点无关。