当 Tkinter Entry 小部件被修改时,如何获得事件回调?

正如问题所述。 Text小部件具有 <<Modified>>事件,但是 Entry小部件似乎没有。

114071 次浏览

向 Entry 小部件添加一个 Tkinter StringVar。

from Tkinter import *


def callback(sv):
print sv.get()


root = Tk()
sv = StringVar()
sv.trace("w", lambda name, index, mode, sv=sv: callback(sv))
e = Entry(root, textvariable=sv)
e.pack()
root.mainloop()

谢谢你,史蒂文!Russell Owen 的 Tkinter Folklore 解释了如何使用 globalgetvar ()直接从 name 参数(PY _ VAR #)获取 StringVar 值,但没有解释如何将名称映射到小部件。修改回调 args 的 lambda 方法非常神奇(至少对我们这些 Python 新手而言)。

当有多个 Entry 时,通常不仅需要知道值,而且还需要知道更改了哪个 Entry。稍微扩展一下 Steven 的示例,下面(Python 3)传递一个索引,该索引可用于跟踪多个条目。

from tkinter import Tk, Frame, Label, Entry, StringVar


class Fruitlist:
def entryupdate(self, sv, i):
print(sv, i, self.fruit[i], sv.get())


def __init__(self, root):
cf = Frame(root)
cf.pack()
self.string_vars = []
self.fruit = ("Apple", "Banana", "Cherry", "Date")
for f in self.fruit:
i = len(self.string_vars)
self.string_vars.append(StringVar())
self.string_vars[i].trace("w", lambda name, index, mode, var=self.string_vars[i], i=i:
self.entryupdate(var, i))
Label(cf, text=f).grid(column=2, row=i)
Entry(cf, width=6, textvariable=self.string_vars[i]).grid(column=4, row=i)


root = Tk()
root.title("EntryUpdate")
app = Fruitlist(root)
root.mainloop()

在撰写本文时(2017年,Python 3.6,tkinter 版本8.6.6) ,文档建议不要使用 .trace。现在建议的形式似乎是:

sv.trace_add("write", callback)

如果您希望在变量更改时得到通知,那么这种方法非常有效。但是,我的应用程序只希望在用户完成编辑文本时得到通知。我发现,“验证”机制在这里起到了很好的作用:

from tkinter import *


root = Tk()
sv = StringVar()


def callback():
print(sv.get())
return True


e = Entry(root, textvariable=sv, validate="focusout", validatecommand=callback)
e.grid()
e = Entry(root)
e.grid()
root.mainloop()

这将在条目小部件失去焦点时调用 callback(我添加了第2个条目小部件,所以第1个条目小部件实际上可能失去焦点!)

我知道其他变种。

在我输入代码之前,它可以更好地解释编码的路径: 阅读 给你

这就是我的原则:

from Tkinter import *


class ttt:
def __init__(self):
self.str1 = StringVar()
self.e1 = Entry(root, textvariable=self.str1)
self.str1.trace('w', self.callback_1)
self.e1.pack()


self.str2 = StringVar()
self.e2 = Entry(root, textvariable=self.str2, state='readonly')
self.e2.pack()


self.str3 = StringVar()
self.e3 = Entry(root, textvariable=self.str3, state='readonly')
self.e3.pack()


bt = Button(root, text = 'ещё', command = self.callback_2)
bt.pack()


def callback_1(self, name='', index='', mode=''):
tmp = self.str1.get()
if tmp:
self.str2.set(int(tmp) * 6)
print self.str2.get()


def callback_2(self, name='', index='', mode=''):
tmp = self.str1.get()
if tmp:
self.str3.set(int(tmp) * 6)
print self.str3.get()


root = Tk()
t = ttt()
root.mainloop()

有2个变体: 按下按钮,进入条目。 现在您可以选择任何变体

我使用 Python 3.6,无法获得。追踪到了。下面的代码允许接受或编辑 StringVar 的默认值。在按下返回键时调用 on _ change。

from tkinter import Tk, LEFT, BOTH, StringVar
from tkinter.ttk import Entry, Frame




class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()


def initUI(self):
self.parent.title("Entry")
self.pack(fill=BOTH, expand=1)
self.contents = StringVar()
# give the StringVar a default value
self.contents.set('test')
self.entry = Entry(self)
self.entry.pack(side=LEFT, padx=15)
self.entry["textvariable"] = self.contents
self.entry.bind('<Key-Return>', self.on_changed)


def on_changed(self, event):
print('contents: {}'.format(self.contents.get()))
return True




def main():
root = Tk()
ex = Example(root)
root.geometry("250x100+300+300")
root.mainloop()




if __name__ == '__main__':
main()

您还可以使用每次用户单击小部件时触发的 KeyRelease 事件。< br > 比筛选更改更多。

from tkinter import *
from tkinter import ttk


class GUI():
def __init__(self):
self.root = Tk()
self.sv = StringVar()
self.prevlaue=''
#entry
self.entry = ttk.Entry(self.root, width=30, textvariable =self.sv)
self.entry.grid(pady=20,padx=20)
self.entry.bind("<KeyRelease>", self.OnEntryClick) #keyup
self.root.mainloop()


def OnEntryClick(self, event):
value=self.sv.get().strip()
changed = True if self.prevlaue != value else False
print(value, 'Text has changed ? {}'.format(changed))
self.prevlaue = value


#create the gui
GUI()

希望能有所帮助。

我发现使用内置的 TK 验证更适合我的初学者级别的 Python 技能。Tkinter 本身没有很好地记录 Entry 元素的验证,您必须使用 register ()方法来设置回调,通过从已注册的验证回调返回 True,您可以获得通知。

def filter_callback(self,new_value):
print(new_value)
# must return true since we want the validation events to keep coming
return(True)


def __init__(self,root)
self.edit_filter = ttk.Entry(root)
# %d = Type of action (1=insert, 0=delete, -1 for others)
# %i = index of char string to be inserted/deleted, or -1
# %P = value of the entry if the edit is allowed
# %s = value of entry prior to editing
# %S = the text string being inserted or deleted, if any
# %v = the type of validation that is currently set
# %V = the type of validation that triggered the callback
#      (key, focusin, focusout, forced)
# %W = the tk name of the widget
vcmd = (self.edit_filter.register(self.filter_callback), "%P")
# notify key presses only
self.edit_filter.config(validate = "key", validatecommand = vcmd)

(只有说明性代码)

示例答案在这里 交互式验证 tkinter 中的 Entry 小部件内容,和一些文档的事件在这里 https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/entry-validation.html。它的代码并不比使用跟踪少,但是对于一些初学者来说可能更容易跟踪和构建。

我已经能够使用 <FocusOut>事件来做我需要做的事情:

import tkinter
from tkinter import ttk


def doSomething(event):
text = event.widget.get()
print(text)


window = tkinter.Tk()
testEntry = ttk.Entry(window)
testEntry.bind('<FocusOut>', doSomething)
testEntry.grid(column=0,row=0)
thingEntry = ttk.Entry(window)
thingEntry.bind('<FocusOut>', doSomething)
thingEntry.grid(column=0,row=1)
startButton = ttk.Button(window,text='OK',command=lambda: print('click'))
startButton.grid(column=0,row=2, sticky='W')


window.mainloop()
print('Bye Bye')

重要的是:

def doSomething(event):
text = event.widget.get()
print(text)


testEntry.bind('<FocusOut>', doSomething)

使用 Entry.bind(event,function)可以附加一个事件侦听器,在本例中是 doSomething函数。这个函数获取一个参数,一个 Event对象。反过来,您可以使用 event.widget.get()读取 Entry小部件中的内容。

每次离开 Entry时,它的值都会被打印出来。这包括单击按钮的时候。正如您将从测试中看到的,按钮的事件是在 Entry事件之后触发的,

这是我对这个基于 @ Avi ba的答案。通过为文本变量添加 setter 和 getter 使其更加简单。也避免使用 StrinVar

class CustomEntry(ttk.Entry):
def __init__(self, parent, valueChangeCallback=lambda x: print(x), **kwargs):
super().__init__(parent, **kwargs)


# bind function callback when value of the text is changed
self.bind("<KeyRelease>", lambda e: valueChangeCallback(self.text))




@property
def text(self) -> str:
return self.get()




@text.setter
def text(self, value) -> None:
self.delete(0, 'end')
self.insert(0, value)