簡體   English   中英

如何使用不會在復制/粘貼時崩潰的代理來跟蹤是否已修改tkinter文本窗口小部件?

[英]How do I track whether a tkinter text widget has been modified using a proxy that doesn't crash on copy/paste?

我使用了前面這個問題的以下解決方案來跟蹤是否修改了tkinter文本小部件:

import tkinter as tk


class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        """A text widget that report on internal widget commands"""
        tk.Text.__init__(self, *args, **kwargs)

        # create a proxy for the underlying widget
        self._orig = self._w + "_orig"
        self.tk.call("rename", self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)

    def _proxy(self, command, *args):
        cmd = (self._orig, command) + args
        result = self.tk.call(cmd)

        if command in ("insert", "delete", "replace"):
            self.event_generate("<<TextModified>>")

        return result


root = tk.Tk()
label = tk.Label(root, anchor="w")
text = CustomText(root, width=40, height=4)

label.pack(side="bottom", fill="x")
text.pack(side="top", fill="both", expand=True)


def onModification(event):
    chars = len(event.widget.get("1.0", "end-1c"))
    label.configure(text="%s chars" % chars)


text.bind("<<TextModified>>", onModification)

root.mainloop()

但是,在將它集成到我自己的代碼中之后,我發現我的代碼和上面的裸解決方案都存在問題。 如果您嘗試粘貼到文本小部件中,整個程序將崩潰。 終端出現以下錯誤:

Traceback (most recent call last):
  File "Test.py", line 39, in <module>
    root.mainloop()
  File "AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1277, in mainloop
    self.tk.mainloop(n)
  File "Test.py", line 16, in _proxy
    result = self.tk.call(cmd)
_tkinter.TclError: text doesn't contain any characters tagged with "sel"

(我刪除了上面文件路徑中的識別信息,但是直接從控制台復制了這些信息。)

經過一些測試后,只要您在粘貼時選擇/突出顯示了一些文本,就可以粘貼而不會崩潰程序。

未修改的文本小部件中不會發生此行為; 您可以正常粘貼而不預先選擇文本,不會崩潰。

所以,我的問題是,如何修改上面的解決方案,使粘貼不會崩潰呢? 我不熟悉Tcl / Tk,所以我不知道如何開始調查這個。 這是在Python 3.6.3中。

(我也會直接聯系這段代碼的原作者,但事實證明這里沒有私人消息功能,我不能以新用戶身份發表評論。)

編輯:我現在有工作代碼,雖然解決方案感覺它與膠帶一起保存,而不是實際解決潛在的問題。 我修改了CustomText類,如下所示:

class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        """A text widget that report on internal widget commands"""
        tk.Text.__init__(self, *args, **kwargs)

        # create a proxy for the underlying widget
        self._orig = self._w + "_orig"
        self.tk.call("rename", self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)
        self.bind("<<Paste>>", self.Paste)

    def _proxy(self, command, *args):
        cmd = (self._orig, command) + args
        result = self.tk.call(cmd)

        if command in ("insert", "delete", "replace"):
            self.event_generate("<<TextModified>>")

        return result

    def Paste(self, event):
        tagranges = self.tag_ranges("sel")
        if tagranges:
            selectionstart = self.index(tk.SEL_FIRST)
            selectionend = self.index(tk.SEL_LAST)
            self.delete(selectionstart, selectionend)
            self.mark_set(tk.INSERT, selectionstart)
        self.insert(tk.INSERT, root.clipboard_get())
        self.see(tk.INSERT)
        return "break"

通過將"<<Paste>>"綁定到以return "break"結尾的函數,我可以阻止小部件將事件傳遞給導致崩潰的任何內容,並且修改事件仍然按預期觸發。 有趣的是,我可以編寫自己的粘貼函數,它在return "break"行之前,並且它起到我認為應該從一開始就具有的作用。

仍然不知道是什么導致這個問題,除了它顯然是一個Windows問題(感謝檢查出來,布萊恩奧克利)。

您可以綁定事件"<Control-v>""<Control-c>"

這意味着如果用戶使用其中任何一個,您可以設置一個特殊條件來以不同方式處理它。

from tkinter import *

root = Tk()

text = Text(root)
text.pack()

def callback(*args):
    print(True)

text.bind("<Control-v>", callback)

root.mainloop()

這更像是一種解決方法而不是解決方案,因為這會讓您對我們不了解的其他鍵盤組合的潛在崩潰開放。

除非有人知道更好的解決方案,否則我會在此留下答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM