【问题标题】:Readonly tkinter text widget只读 tkinter 文本小部件
【发布时间】:2014-03-19 08:28:22
【问题描述】:

我想将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")

这里的错误是用户输入的最后一个字符被添加到脚本中。我怀疑原因是调用了只读函数,then 用户输入被写入小部件。如何颠倒这个顺序并让 readonly 函数被调用 after 用户输入被写入小部件?

有什么提示吗?

【问题讨论】:

  • 什么是 ScrolledText 小部件?我不熟悉它...
  • 设置state=DISABLED怎么样?
  • 类似于带有滚动条的tkinter text widgetdocs.python.org/2/library/scrolledtext.html
  • 如果 state=DISABLED,您将无法选择和复制部分文本
  • 您在插入字符之前重置文本。请改用&lt;KeyRelease&gt;。但我想仍然必须有更好的方法......也许this 有帮助?

标签: python text tkinter


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    请注意,仅更改了绑定。所有文本命令(如插入、删除等)仍然可用。

    【讨论】:

    • 为什么不将小部件的state 属性设置为"disabled"
    • “禁用”状态禁用了很多东西。每个更改文本内容的操作都被禁用。插入/删除命令将不再起作用。同理,文本不会响应“>”绑定
    【解决方案3】:

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

      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)
    

    【讨论】:

    • 如果您在小部件中输入了多个字符,这将不起作用。当然,它不会阻止您删除字符。此外,如果文本小部件以外的任何内容具有焦点,它将引发错误。
    • 是的,但如果您只需要在不将捕获的字符写入小部件的情况下捕获关键事件的能力,这仍然是一个有用的解决方案。
    猜你喜欢
    • 2011-04-20
    • 1970-01-01
    • 2018-10-01
    • 2019-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多