有没有一种方法可以使Tkinter文本小部件只读?

93

看起来它没有那个属性,但对我来说非常有用。


14
一个Tkinter的Entry小部件可以使用entry.config(state='readonly')来设置只读状态。不幸的是,这种方法似乎不适用于Text小部件。 - Craig McQueen
13个回答

117

在使用text.insert()text.bind()后,你需要将Text小部件的状态NORMAL更改为DISABLED

text.config(state=DISABLED)

15
那么你就不能选择文字并复制它。 - Craig McQueen
3
使用CTRL-C(在Windows中)或自动复制(在Linux中)选择和复制似乎对我来说都很有效。 - Dologan
15
您可以通过将 <1> 与一个设置文本小部件焦点的函数绑定来实现:text.bind("<1>", lambda event: text.focus_set()) - nbro

49
text = Text(app, state='disabled', width=44, height=5)

在插入之前和之后改变状态,否则它将不会更新

text.configure(state='normal')
text.insert('end', 'Some Text')
text.configure(state='disabled')

36
很简单的解决方案就是将任何按键绑定到一个返回"break"的函数上,像这样:

非常简单的解决方案就是将任何按键绑定到一个返回"break"的函数上,如下所示:

import Tkinter

root = Tkinter.Tk() 

readonly = Tkinter.Text(root)
readonly.bind("<Key>", lambda e: "break")

哇!这个真不错。 - WhyWhat

25

Tcl wiki详细描述了这个问题,并列出了三个可能的解决方案:

  1. 在其他答案中描述的禁用/启用技巧。
  2. 替换插入/删除事件的绑定。
  3. 与(2)相同,但将其包装在单独的小部件中。

(2)或(3)是更可取的,然而解决方案并不明显。 但是,在unpythonic wiki上有一个可用的解决方案

 from Tkinter import Text
 from idlelib.WidgetRedirector import WidgetRedirector

 class ReadOnlyText(Text):
     def __init__(self, *args, **kwargs):
         Text.__init__(self, *args, **kwargs)
         self.redirector = WidgetRedirector(self)
         self.insert = self.redirector.register("insert", lambda *args, **kw: "break")
         self.delete = self.redirector.register("delete", lambda *args, **kw: "break")

2
idlelib是什么以及它从哪里来?最好有一个不需要idlelib依赖的解决方案。 - Craig McQueen
在Ubuntu Linux上,我可以通过执行sudo apt-get install idle-python2.7来获取idlelib - Craig McQueen
5
idlelib 是 Python 标准库的一部分。然而,由于某些原因,Ubuntu 似乎喜欢将 Python 包装成许多小部分。 - freakboy3742

11

如果您的使用案例非常简单,nbro's text.bind('<1>', lambda event: text.focus_set()) 代码可以解决 Craig McQueen 在 OS X 上看到但其他人在 Windows 和 Linux 上没有看到的交互问题。

另一方面,如果您的只读数据具有任何上下文结构,则在某些时候您可能会使用 Tkinter.Text.insert(position, text, taglist) 将其添加到只读的 Text 框窗口中,并使用标签进行标记。这样做是因为您希望根据上下文使部分数据突出显示。使用标记标记的文本可以通过调用 .Text.tag_config() 更改字体或颜色等方式进行强调。同样,使用标记标记的文本可以使用 .Text.tag_bind() 附加交互绑定。这里有一个很好的使用 这些函数 的示例。如果一个 mark_for_paste() 函数不错,那么能够理解您的数据上下文的 mark_for_paste() 函数可能更好。

8

这是我做的方式。在结束时将状态设置为disabled,禁止用户编辑文本框,但在编辑文本框之前必须将状态设置为normal,以便插入文本。

from tkinter import *
text=Text(root)
text.pack()
text.config(state="normal")
text.insert(END, "Text goes here")
text.config(state="disabled")

7
from Tkinter import *
root = Tk()
text = Text(root)
text.insert(END,"Some Text")
text.configure(state='disabled')

3
那么你就无法选择文本并复制它。 - Craig McQueen
3
你可以选择文本并复制。这在 Windows 上对我有效。 - manty
1
@CraigMcQueen - 我相当确定这是在内部处理的,无论状态如何。我不知道是否可以禁用选择和复制。 - Colby Gallup

5

如果您想在Windows中禁用用户编辑并允许使用Ctrl+C复制屏幕文本,请使用以下代码:

def txtEvent(event):
    if(event.state==12 and event.keysym=='c' ):
        return
    else:
        return "break"

txt.bind("<Key>", lambda e: txtEvent(e))

4

如果您不需要选择文本,则禁用状态是最简单的方法。为了支持复制,您可以使用一个外部实体 - Button - 来完成工作。每当用户按下按钮时,Text 的内容将被复制到剪贴板中。Tk 有处理剪贴板的内置支持(请参见此处),因此模仿Ctrl-C的行为是一项容易的任务。如果您正在构建例如写入日志消息的控制台,您可以进一步添加一个Entry,在那里用户可以指定他想要复制的日志消息数目。


3

许多人提到当状态被禁用时,你无法从文本部件中复制。在我的Ubuntu Python 3.8.5上,复制问题的原因是Ubuntu中该部件没有焦点(在Windows上工作)。

当我需要通过编程方式进行编辑时,我一直在使用将状态设置为禁用的解决方案,并转换状态,使用以下步骤:1)text.config(state=tkinter.NORMAL) 2)编辑文本和 3)text.config(state=tkinter.DISABLED)。在Windows上,我可以正常地从小部件中复制文本,但在Ubuntu上,它看起来像我已经选择了文本,但我无法复制它。

经过一些测试,结果发现只要文本部件拥有焦点,我就可以复制它。在Windows上,当你点击它时,似乎文本部件会获得焦点,而在Ubuntu上,点击文本部件并不会使其聚焦。

所以我通过将text.focus_set()绑定到鼠标点击事件"<Button>"来解决这个问题:

import tkinter
root = tkinter.Tk()
text0 = tkinter.Text(root, state=tkinter.DISABLED)
text0.config(state=tkinter.NORMAL)
text0.insert(1.0, 'You can not copy or edit this text.')
text0.config(state=tkinter.DISABLED)
text0.pack()

text1 = tkinter.Text(root, state=tkinter.DISABLED)
text1.config(state=tkinter.NORMAL)
text1.insert(1.0, 'You can copy, but not edit this text.')
text1.config(state=tkinter.DISABLED)
text1.bind("<Button>", lambda event: text1.focus_set())
text1.pack()

对我来说,这证明是一个简单而有效的解决方案,希望其他人也会发现它有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接