有沒有辦法添加撤銷和重做功能在Tkinter Entry
部件或必須使用單線Text
小部件這種類型的功能?在Tkinter Entry小部件中撤消和重做?
如果是後者,那麼在配置Text
小部件以充當Entry
小部件時,我應該遵循哪些提示?
某些可能需要調整的功能包括捕獲Return
KeyPress
,將tab按鍵轉換爲改變焦點的請求,以及從剪貼板粘貼的文本中刪除換行符。
有沒有辦法添加撤銷和重做功能在Tkinter Entry
部件或必須使用單線Text
小部件這種類型的功能?在Tkinter Entry小部件中撤消和重做?
如果是後者,那麼在配置Text
小部件以充當Entry
小部件時,我應該遵循哪些提示?
某些可能需要調整的功能包括捕獲Return
KeyPress
,將tab按鍵轉換爲改變焦點的請求,以及從剪貼板粘貼的文本中刪除換行符。
聲明:這些只是我想到的如何實現它的想法。
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)
當心根據需要設置鎖。
更新使用此方法撤銷/重做:
我創造與很多幀的圖形用戶界面,並且每個含有至少十個或更多的「進入」小部件。 我使用了History類,併爲每個輸入字段創建了一個歷史對象。我能夠將所有的入口小部件值存儲在列表中,就像這裏所做的那樣。 我使用附加到每個條目小部件的'trace'方法,它將調用History類的'add'功能並存儲每個更改。通過這種方式,我無需單獨運行任何線程即可完成。 但是這樣做的最大缺點是,我們無法用這種方法做多個undos/redos。
問題: 當我跟蹤條目窗口小部件的每一次更改並將其添加到列表中時,它還'追蹤'我們'撤銷/重做'時發生的變化,這意味着我們不能再退一步。一旦你做了撤銷,這是一個將被追蹤的變化,因此'撤消'值將被添加到列表中。因此這不是正確的方法。
解決方案: 完成此操作的最佳方法是爲每個條目窗口小部件創建兩個堆棧。一個用於'撤銷',一個用於'重做'。當條目發生變化時,將該值推入撤消堆棧。當用戶按下撤消時,從撤消堆棧中彈出最後存儲的值,並且重要地將其推送到「重做堆棧」。因此,當用戶按下重做時,從重做堆棧彈出最後一個值。
檢查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
在這裏使用線程是完全沒有必要的。小部件可以在更改時輕鬆通知您。 – 2010-11-10 20:46:00
@Bryan Oakley:不是真的,這只是解決問題的一種不同方法,也是很多解決方案之一。 – 2010-11-11 01:34:59
@MG:良好的計劃:捕捉每一個部件的改變,將總值存儲在歷史列表中,撤消時,將歷史指針移回一級並獲取其值。重做時,將歷史指針向前移動一級並獲取其值。存儲用戶更改時,請在每次更改用戶後清除重做歷史記錄。我不是線程監視的粉絲 - 我需要的是相當於值改變事件(或可能trace_var()),以便我可以跟蹤確切的更改。我發佈了一個關於這樣的事件存在的問題:http://stackoverflow.com/questions/4165164 – Malcolm 2010-11-12 13:48:08