<p>如果您愿意以危险的方式生活,那么就有可能连接到文本小部件的内部,并让它在内容更改时调用函数,而不管它如何更改。</p>
<p>诀窍是用代理替换底层的tk小部件命令。这个代理负责执行真实文本小部件将要执行的任何操作,然后发送一个虚拟事件(如果它所做的是插入或删除文本)。</p>
<p>有了它,只需要设置一个绑定到该事件,并在变量上放置一个读取跟踪。当然,如果尝试在文本中插入小部件或图像,它们将不会反映在textvariable中。</p>
<p>这里有一个快速而肮脏的例子,没有在任何真实的测试中。这使用了与我在文本小部件中实现行号相同的技术(请参见<a href="https://stackoverflow.com/a/16375233">https://stackoverflow.com/a/16375233</a>)</p>
<pre><code>import Tkinter as tk
import random
import timeit
class TextWithVar(tk.Text):
'''A text widget that accepts a 'textvariable' option'''
def __init__(self, parent, *args, **kwargs):
try:
self._textvariable = kwargs.pop("textvariable")
except KeyError:
self._textvariable = None
tk.Text.__init__(self, parent, *args, **kwargs)
# if the variable has data in it, use it to initialize
# the widget
if self._textvariable is not None:
self.insert("1.0", self._textvariable.get())
# this defines an internal proxy which generates a
# virtual event whenever text is inserted or deleted
self.tk.eval('''
proc widget_proxy {widget widget_command args} {
# call the real tk widget command with the real args
set result [uplevel [linsert $args 0 $widget_command]]
# if the contents changed, generate an event we can bind to
if {([lindex $args 0] in {insert replace delete})} {
event generate $widget <<Change>> -when tail
}
# return the result from the real widget command
return $result
}
''')
# this replaces the underlying widget with the proxy
self.tk.eval('''
rename {widget} _{widget}
interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
'''.format(widget=str(self)))
# set up a binding to update the variable whenever
# the widget changes
self.bind("<<Change>>", self._on_widget_change)
# set up a trace to update the text widget when the
# variable changes
if self._textvariable is not None:
self._textvariable.trace("wu", self._on_var_change)
def _on_var_change(self, *args):
'''Change the text widget when the associated textvariable changes'''
# only change the widget if something actually
# changed, otherwise we'll get into an endless
# loop
text_current = self.get("1.0", "end-1c")
var_current = self._textvariable.get()
if text_current != var_current:
self.delete("1.0", "end")
self.insert("1.0", var_current)
def _on_widget_change(self, event=None):
'''Change the variable when the widget changes'''
if self._textvariable is not None:
self._textvariable.set(self.get("1.0", "end-1c"))
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.textvar = tk.StringVar()
self.textvar.set("Hello, world!")
# create an entry widget and a text widget that
# share a textvariable; typing in one should update
# the other
self.entry = tk.Entry(self, textvariable=self.textvar)
self.text = TextWithVar(self,textvariable=self.textvar,
borderwidth=1, relief="sunken",
background="bisque")
self.entry.pack(side="top", fill="x", expand=True)
self.text.pack(side="top",fill="both", expand=True)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
</code></pre>