Tkinter - 撤消和重做对文本小部件的格式化
Tkinter - Undo and Redo the formatting's made to a text widget
有什么方法可以撤消和重做对 tkinter 文本小部件所做的格式化吗?
代码如下:
from tkinter import *
root = Tk()
text = Text(root, width=65, height=20, undo=True, font="consolas 14")
text.pack()
undo_button = Button(root, text="Undo", command=text.edit_undo)
undo_button.pack()
redo_button = Button(root, text="Redo", command=text.edit_redo)
redo_button.pack()
text.insert('1.0', "Hello world")
text.tag_add('test', '1.0', '1.5')
text.tag_config('test', background='yellow')
mainloop()
在这里,我给文本小部件添加了一个标签,但问题是,当我点击撤消或重做按钮时,只有文本小部件中的文本被修改,而不是格式化它。
我想要的是当我按下 undo_button
时 首先 应该删除 'test'
标签,因为添加该标签是我最后一件事对我的文本小部件做了。
有什么方法可以在 tkinter 中实现吗?
如果有人能帮助我就太好了。
我认为默认的 tkinter 安装是不可能的,因为 tcl 只在 undoing/redoing 操作时才考虑文本。它不关心标签。这就是为什么(在您的示例代码中),如果您尝试重做撤消,文本不会突出显示。重写了 tcl 如何处理 undos/redos suggested in the past,但我找不到关于这些建议的任何进展。
可能可以安装类似 this but it is out of my expertise. In there 的东西,它讨论撤消机制和标签,我认为这就是您要找的东西。更具体地说,修改后的文本小部件添加了一个新的 immediately
参数:“如果指定选项 -immediately,则将立即推送分隔符;如果标记或标签操作应该分隔,则这是必需的。”。 =18=] 因此,它可能会在标记添加之间添加分隔符。
正如 TheLizzard 所说,Text
小部件的内置 undo/redo 机制无法做到这一点。但是,您可以实施自己的 undo/redo 格式设置机制。
想法是有一个撤销堆栈和一个重做堆栈。我对两者都使用了列表。当您调用撤消函数时,您获取撤消堆栈中的最后一项,将其附加到重做堆栈并使用该项目中的信息来撤消修改。它通常是一个 (undo_args, redo_args)
元组。然后对于重做函数,你做同样的事情,但从重做堆栈中取出项目并将其附加到撤消堆栈。
但是,您需要在每次发生修改时将所需的 (undo_args, redo_args)
附加到撤消堆栈,并清除重做堆栈。
为此,我从 Brayn Oakley 的回答 (关于自动更新行号)中改编了代理机制。
每次 Text
小部件发生修改时,都会调用 _proxy
方法。如果此修改是文本插入、文本删除、标记添加或标记删除,则 (undo_args, redo_args)
元组附加到撤消堆栈。因此可以通过调用 self.tk.call((self._orig,) + undo_args)
撤消修改并使用 self.tk.call((self._orig,) + redo_args)
.
重做
import tkinter as tk
class MyText(tk.Text):
def __init__(self, master=None, **kw):
tk.Text.__init__(self, master, undo=False, **kw)
self._undo_stack = []
self._redo_stack = []
# create proxy
self._orig = self._w + "_orig"
self.tk.call("rename", self._w, self._orig)
self.tk.createcommand(self._w, self._proxy)
def _proxy(self, *args):
if args[0] in ["insert", "delete"]:
if args[1] == "end":
index = self.index("end-1c")
else:
index = self.index(args[1])
if args[0] == "insert":
undo_args = ("delete", index, "{}+{}c".format(index, len(args[2])))
else: # args[0] == "delete":
undo_args = ("insert", index, self.get(*args[:1]))
self._redo_stack.clear()
self._undo_stack.append((undo_args, args))
elif args[0] == "tag":
if args[1] in ["add", "remove"] and args[2] != "sel":
indexes = tuple(self.index(ind) for ind in args[3:])
undo_args = ("tag", "remove" if args[1] == "add" else "add", args[2]) + indexes
self._redo_stack.clear()
self._undo_stack.append((undo_args, args))
result = self.tk.call((self._orig,) + args)
return result
def undo(self):
if not self._undo_stack:
return
undo_args, redo_args = self._undo_stack.pop()
self._redo_stack.append((undo_args, redo_args))
self.tk.call((self._orig,) + undo_args)
def redo(self):
if not self._redo_stack:
return
undo_args, redo_args = self._redo_stack.pop()
self._undo_stack.append((undo_args, redo_args))
self.tk.call((self._orig,) + redo_args)
root = tk.Tk()
text = MyText(root, width=65, height=20, font="consolas 14")
text.pack()
undo_button = tk.Button(root, text="Undo", command=text.undo)
undo_button.pack()
redo_button = tk.Button(root, text="Redo", command=text.redo)
redo_button.pack()
text.insert('end', "Hello world")
text.tag_add('test', '1.0', '1.5')
text.tag_config('test', background='yellow')
root.mainloop()
有什么方法可以撤消和重做对 tkinter 文本小部件所做的格式化吗?
代码如下:
from tkinter import *
root = Tk()
text = Text(root, width=65, height=20, undo=True, font="consolas 14")
text.pack()
undo_button = Button(root, text="Undo", command=text.edit_undo)
undo_button.pack()
redo_button = Button(root, text="Redo", command=text.edit_redo)
redo_button.pack()
text.insert('1.0', "Hello world")
text.tag_add('test', '1.0', '1.5')
text.tag_config('test', background='yellow')
mainloop()
在这里,我给文本小部件添加了一个标签,但问题是,当我点击撤消或重做按钮时,只有文本小部件中的文本被修改,而不是格式化它。
我想要的是当我按下 undo_button
时 首先 应该删除 'test'
标签,因为添加该标签是我最后一件事对我的文本小部件做了。
有什么方法可以在 tkinter 中实现吗?
如果有人能帮助我就太好了。
我认为默认的 tkinter 安装是不可能的,因为 tcl 只在 undoing/redoing 操作时才考虑文本。它不关心标签。这就是为什么(在您的示例代码中),如果您尝试重做撤消,文本不会突出显示。重写了 tcl 如何处理 undos/redos suggested in the past,但我找不到关于这些建议的任何进展。
可能可以安装类似 this but it is out of my expertise. In there 的东西,它讨论撤消机制和标签,我认为这就是您要找的东西。更具体地说,修改后的文本小部件添加了一个新的 immediately
参数:“如果指定选项 -immediately,则将立即推送分隔符;如果标记或标签操作应该分隔,则这是必需的。”。 =18=] 因此,它可能会在标记添加之间添加分隔符。
正如 TheLizzard 所说,Text
小部件的内置 undo/redo 机制无法做到这一点。但是,您可以实施自己的 undo/redo 格式设置机制。
想法是有一个撤销堆栈和一个重做堆栈。我对两者都使用了列表。当您调用撤消函数时,您获取撤消堆栈中的最后一项,将其附加到重做堆栈并使用该项目中的信息来撤消修改。它通常是一个 (undo_args, redo_args)
元组。然后对于重做函数,你做同样的事情,但从重做堆栈中取出项目并将其附加到撤消堆栈。
但是,您需要在每次发生修改时将所需的 (undo_args, redo_args)
附加到撤消堆栈,并清除重做堆栈。
为此,我从 Brayn Oakley 的回答 (关于自动更新行号)中改编了代理机制。
每次 Text
小部件发生修改时,都会调用 _proxy
方法。如果此修改是文本插入、文本删除、标记添加或标记删除,则 (undo_args, redo_args)
元组附加到撤消堆栈。因此可以通过调用 self.tk.call((self._orig,) + undo_args)
撤消修改并使用 self.tk.call((self._orig,) + redo_args)
.
import tkinter as tk
class MyText(tk.Text):
def __init__(self, master=None, **kw):
tk.Text.__init__(self, master, undo=False, **kw)
self._undo_stack = []
self._redo_stack = []
# create proxy
self._orig = self._w + "_orig"
self.tk.call("rename", self._w, self._orig)
self.tk.createcommand(self._w, self._proxy)
def _proxy(self, *args):
if args[0] in ["insert", "delete"]:
if args[1] == "end":
index = self.index("end-1c")
else:
index = self.index(args[1])
if args[0] == "insert":
undo_args = ("delete", index, "{}+{}c".format(index, len(args[2])))
else: # args[0] == "delete":
undo_args = ("insert", index, self.get(*args[:1]))
self._redo_stack.clear()
self._undo_stack.append((undo_args, args))
elif args[0] == "tag":
if args[1] in ["add", "remove"] and args[2] != "sel":
indexes = tuple(self.index(ind) for ind in args[3:])
undo_args = ("tag", "remove" if args[1] == "add" else "add", args[2]) + indexes
self._redo_stack.clear()
self._undo_stack.append((undo_args, args))
result = self.tk.call((self._orig,) + args)
return result
def undo(self):
if not self._undo_stack:
return
undo_args, redo_args = self._undo_stack.pop()
self._redo_stack.append((undo_args, redo_args))
self.tk.call((self._orig,) + undo_args)
def redo(self):
if not self._redo_stack:
return
undo_args, redo_args = self._redo_stack.pop()
self._undo_stack.append((undo_args, redo_args))
self.tk.call((self._orig,) + redo_args)
root = tk.Tk()
text = MyText(root, width=65, height=20, font="consolas 14")
text.pack()
undo_button = tk.Button(root, text="Undo", command=text.undo)
undo_button.pack()
redo_button = tk.Button(root, text="Redo", command=text.redo)
redo_button.pack()
text.insert('end', "Hello world")
text.tag_add('test', '1.0', '1.5')
text.tag_config('test', background='yellow')
root.mainloop()