如果是后者,则在配置Text小部件以充当Entry小部件时应遵循哪些提示?
可能需要调整的一些功能包括捕获Return KeyPress,将Tab键按下转换为请求更改焦点,并从剪贴板粘贴的文本中删除换行符。
请查看Tkinter自定义输入框。我已经添加了剪切、复制、粘贴上下文菜单以及撤销和重做功能。
# -*- coding: utf-8 -*-
from tkinter import *
class CEntry(Entry):
def __init__(self, parent, *args, **kwargs):
Entry.__init__(self, parent, *args, **kwargs)
self.changes = [""]
self.steps = int()
self.context_menu = Menu(self, tearoff=0)
self.context_menu.add_command(label="Cut")
self.context_menu.add_command(label="Copy")
self.context_menu.add_command(label="Paste")
self.bind("<Button-3>", self.popup)
self.bind("<Control-z>", self.undo)
self.bind("<Control-y>", self.redo)
self.bind("<Key>", self.add_changes)
def popup(self, event):
self.context_menu.post(event.x_root, event.y_root)
self.context_menu.entryconfigure("Cut", command=lambda: self.event_generate("<<Cut>>"))
self.context_menu.entryconfigure("Copy", command=lambda: self.event_generate("<<Copy>>"))
self.context_menu.entryconfigure("Paste", command=lambda: self.event_generate("<<Paste>>"))
def undo(self, event=None):
if self.steps != 0:
self.steps -= 1
self.delete(0, END)
self.insert(END, self.changes[self.steps])
def redo(self, event=None):
if self.steps < len(self.changes):
self.delete(0, END)
self.insert(END, self.changes[self.steps])
self.steps += 1
def add_changes(self, event=None):
if self.get() != self.changes[-1]:
self.changes.append(self.get())
self.steps += 1
class History(object):
def __init__(self):
self.l = ['']
self.i = 0
def next(self):
if self.i == len(self.l):
return None
self.i += 1
return self.l[self.i]
def prev(self):
if self.i == 0:
return None
self.i -= 1
return self.l[self.i]
def add(self, s):
del self.l[self.i+1:]
self.l.append(s)
self.i += 1
def current(self):
return self.l[self.i]
运行一个线程,每X秒(0.5?)保存条目的状态:
history = History()
...
history.add(stringval.get())
Return
”键的压力。prev = history.prev()
if prev is not None:
stringvar.set(prev)
或者
next = history.next()
if next is not None:
stringvar.set(next)
请注意根据需要设置锁定。
关于使用此方法进行撤销/重做的更新:
我正在创建一个GUI,其中包含许多帧,每个帧至少包含十个或更多的“entry”小部件。我使用了History类并为每个输入字段创建了一个历史对象。我能够像这里一样将所有输入小部件的值存储在列表中。我使用附加到每个输入小部件的“trace”方法调用History类的“add”函数并存储每个更改。通过这种方式,我能够在不运行任何线程的情况下完成它。但是,使用此方法的最大缺点是,我们不能使用多个撤销/重做。
问题: 当我跟踪每个输入小部件的每个更改并将其添加到列表中时,它还会“跟踪”发生在撤销/重做时的更改,这意味着我们无法再向后退一步。一旦您执行撤销操作,它就是一个将被跟踪的更改,因此“撤销”值将在最后添加到列表中。因此,这不是正确的方法。
解决方案: 进行此操作的完美方法是为每个输入小部件创建两个堆栈。一个用于“撤销”,一个用于“重做”。每当输入发生更改时,请将该值推入撤销堆栈。当用户按下撤销时,请从撤销堆栈中弹出最后存储的值,并重要地将其推入“重做”堆栈。因此,当用户按下重做时,请从重做堆栈中弹出最后一个值。
基于 Evgeny 的答案,使用自定义的 Entry
,但添加了一个 tkinter 的 StringVar
,并使用 trace 跟踪小部件,以更准确地跟踪其内容何时发生更改(而不仅仅是按下任何键,这似乎会向堆栈中添加空的撤消/重做项)。还使用 Python deque 添加了最大深度。
如果我们通过代码而不是键盘输入来更改 Entry 的内容,我们可以暂时禁用 trace(例如,请参见下面的 undo
方法)。
代码:
class CEntry(tk.Entry):
def __init__(self, master, **kw):
super().__init__(master=master, **kw)
self._undo_stack = deque(maxlen=100)
self._redo_stack = deque(maxlen=100)
self.bind("<Control-z>", self.undo)
self.bind("<Control-y>", self.redo)
# traces whenever the Entry's contents are changed
self.tkvar = tk.StringVar()
self.config(textvariable=self.tkvar)
self.trace_id = self.tkvar.trace("w", self.on_changes)
self.reset_undo_stacks()
# USE THESE TO TURN TRACE OFF THEN BACK ON AGAIN
# self.tkvar.trace_vdelete("w", self.trace_id)
# self.trace_id = self.tkvar.trace("w", self.on_changes)
def undo(self, event=None): # noqa
if len(self._undo_stack) <= 1:
return
content = self._undo_stack.pop()
self._redo_stack.append(content)
content = self._undo_stack[-1]
self.tkvar.trace_vdelete("w", self.trace_id)
self.delete(0, tk.END)
self.insert(0, content)
self.trace_id = self.tkvar.trace("w", self.on_changes)
def redo(self, event=None): # noqa
if not self._redo_stack:
return
content = self._redo_stack.pop()
self._undo_stack.append(content)
self.tkvar.trace_vdelete("w", self.trace_id)
self.delete(0, tk.END)
self.insert(0, content)
self.trace_id = self.tkvar.trace("w", self.on_changes)
def on_changes(self, a=None, b=None, c=None): # noqa
self._undo_stack.append(self.tkvar.get())
self._redo_stack.clear()
def reset_undo_stacks(self):
self._undo_stack.clear()
self._redo_stack.clear()
self._undo_stack.append(self.tkvar.get())