繁体   English   中英

Readonly tkinter文本小部件

[英]Readonly tkinter text widget

我想使用tkinter text widget作为readonly小部件。 它应该作为transcript区域。 我的想法是将此脚本保存在一个file ,每当用户写入任何内容时,只需删除该小部件的所有内容,然后重新重写即可。

代码如下所示:

transcript_entry = SimpleEditor()  # SimpleEditor is inherited from ScrolledText
transcript_entry.text.delete("1.0", END)

# this is just a test string, it should be the contents of the transcript file
transcript_entry.text.insert("1.0", "This is test transcript")  
transcript_entry.text.bind("<KeyPress>", transcript_entry.readonly)

readonly函数看起来像:

def readonly(self, event):
    self.text.delete("1.0", END)
    # this is just a test string, it should be the contents of the transcript file
    self.text.insert("1.0", "This is test transcript")

这里的错误是用户输入的最后一个字符被添加到记录中。 我怀疑原因是调用了readonly函数, then将用户输入写入窗口小部件。 如何反转此顺序并after将用户输入写入窗口小部件after调用readonly函数?

任何提示?

插入最后一个字符的原因是因为默认绑定(导致插入)发生放在窗口小部件上的自定义绑定之后 因此,首先触发绑定, 然后默认绑定插入字符。 这里还有其他问题和答案,可以更深入地讨论这个问题。 例如,请参阅https://stackoverflow.com/a/11542200/

但是,有一种更好的方法可以完成您想要做的事情。 如果要创建只读文本窗口小部件,可以将state属性设置为"disabled" 这将阻止所有插入和删除(并且意味着您需要在以编程方式输入数据时还原状态)。

在某些平台上,您似乎无法突出显示和复制文本,但这只是因为默认情况下窗口小部件不会专注于鼠标单击。 通过添加绑定来设置焦点,用户可以突出显示和复制文本,但无法剪切或插入。

这是使用python 2.x的一个例子; 对于3.x,您只需更改导入:

import Tkinter as tk
from ScrolledText import ScrolledText

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        t = ScrolledText(self, wrap="word")
        t.insert("end", "Hello\nworld")
        t.configure(state="disabled")
        t.pack(side="top", fill="both", expand=True)

        # make sure the widget gets focus when clicked
        # on, to enable highlighting and copying to the
        # clipboard.
        t.bind("<1>", lambda event: t.focus_set())

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

请不要删除并重新插入文本:

  • 这是一个巨大的性能问题。
  • 它将删除文本上设置的任何标记和标记
  • 这对用户是可见的,用户不喜欢闪烁的界面
  • 这不是必需的,Tkinter可以自定义,只是不允许用户更改内容。

我发现创建只读文本的最佳方法是禁用导致文本更改的所有绑定。

我的解决方案是创建一个只包含“只读命令”的新Widget绑定映射。 然后,只需重新配置您的小部件以使用新的RO绑定映射而不是默认的:

from Tkinter import *

# This is the list of all default command in the "Text" tag that modify the text
commandsToRemove = (
"<Control-Key-h>",
"<Meta-Key-Delete>",
"<Meta-Key-BackSpace>",
"<Meta-Key-d>",
"<Meta-Key-b>",
"<<Redo>>",
"<<Undo>>",
"<Control-Key-t>",
"<Control-Key-o>",
"<Control-Key-k>",
"<Control-Key-d>",
"<Key>",
"<Key-Insert>",
"<<PasteSelection>>",
"<<Clear>>",
"<<Paste>>",
"<<Cut>>",
"<Key-BackSpace>",
"<Key-Delete>",
"<Key-Return>",
"<Control-Key-i>",
"<Key-Tab>",
"<Shift-Key-Tab>"
)


class ROText(Text):
    tagInit = False

    def init_tag(self):
        """
        Just go through all binding for the Text widget.
        If the command is allowed, recopy it in the ROText binding table.
        """
        for key in self.bind_class("Text"):
            if key not in commandsToRemove:
                command = self.bind_class("Text", key)
                self.bind_class("ROText", key, command)
        ROText.tagInit = True


    def __init__(self, *args, **kwords):
        Text.__init__(self, *args, **kwords)
        if not ROText.tagInit:
            self.init_tag()

        # Create a new binding table list, replace the default Text binding table by the ROText one
        bindTags = tuple(tag if tag!="Text" else "ROText" for tag in self.bindtags())
        self.bindtags(bindTags)

text = ROText()

text.insert("1.0", """A long text with several
lines
in it""")


text.pack()

text.mainloop()

请注意,只是更改了绑定。 所有Text命令(如insert,delete,...)仍然可用。

我最近使用了一种不同的,稍微简单的解决方案。 可以添加一个函数来删除所有输入字符,而不是更改所有绑定:

  def read_only(self, event):
    if event.char is not '':  # delete only if the key pressed
                              # corresponds to an actual character
      self.text.delete('insert-1c')

并将其绑定到任何事件:

  root.bind('<Key>', self.read_only)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM