Readonly tkinter文本小部件

时间:2014-02-19 06:53:08

标签: python text tkinter

我想将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用户输入被写入窗口小部件。如何扭转此顺序&amp;让readonly函数调用after将用户输入写入小部件?

任何提示?

3 个答案:

答案 0 :(得分:5)

插入最后一个字符的原因是因为默认绑定(导致插入)在放置在窗口小部件上的自定义绑定之后发生。因此,首先激活绑定,然后然后默认绑定将插入字符。这里还有其他问题和答案,可以更深入地讨论这个问题。例如,请参阅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()

答案 1 :(得分:1)

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

  • 这是一个巨大的性能问题。
  • 它将删除文本
  • 上设置的所有标记和标记
  • 这对用户是可见的,用户不喜欢闪烁的界面
  • 这不是必需的,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,...)仍然可用。

答案 2 :(得分:-1)

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

  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)