简体   繁体   English

如何使 tkinter 文本小部件无法选择?

[英]How can I make a tkinter text widget unselectable?

I want to make my tkinter Text to be only an output and not an input.我想让我的 tkinter Text只是一个输出而不是一个输入。 Through some research I've found that text.config(state="disabled") disables user input, but it still allows for selecting text, which I do not want.通过一些研究,我发现text.config(state="disabled")禁用了用户输入,但它仍然允许选择我不想要的文本。

How can I get my Text widget to be unselectable and unwritable?如何让我的Text小部件不可选择和不可写?

The simplest way is to replace the default text bindings that support selection so that they do nothing.最简单的方法是替换支持选择的默认文本绑定,以便它们什么都不做。 There are a couple ways to do this: using binding tags you can remove all default bindings, or you can remove the bindings to only a subset of default bindings.有几种方法可以做到这一点:使用绑定标签可以删除所有默认绑定,或者您可以只删除默认绑定子集的绑定。

Removing all default bindings删除所有默认绑定

All bindings on widgets -- including the default bindings -- are associated with binding tags (also called "bindtags").小部件上的所有绑定——包括默认绑定——都与绑定标签(也称为“绑定标签”)相关联。 The binding tag for the the text widget is "Text", and all default bindings for the text widget are associated with this tag.文本小部件的绑定标签是“Text”,文本小部件的所有默认绑定都与此标签相关联。 If you remove that binding tag, you remove all Text-specific bindings.如果删除该绑定标记,则会删除所有特定于文本的绑定。

The default binding tags for any widget is a tuple of the string representation of the widget, the internal widget class (in this case, "Text"), the internal name of the toplevel window (in this case, root), and the special tag "all".任何小部件的默认绑定标签是小部件的字符串表示、内部小部件类(在本例中为“Text”)、顶级窗口的内部名称(在本例中为 root)和特殊窗口的元组标记“全部”。

In the following example we change the binding tags so that "Text" is not included, effectively removing all default bindings on the text widget:在下面的示例中,我们更改了绑定标签,以便不包含“Text”,从而有效地删除了文本小部件上的所有默认绑定:

import tkinter as tk

root = tk.Tk()
text = tk.Text(root)
text.bindtags((str(text), str(root), "all"))

Removing specific bindings删除特定绑定

If you prefer to keep some of the default bindings, you can replace just the ones that you don't want.如果您希望保留一些默认绑定,您可以只替换您不想要的那些。 You do that by creating your own bindings, and having those bindings return the string "break".您可以通过创建自己的绑定来实现这一点,并让这些绑定返回字符串“break”。 This special return value tells tkinter to stop processing the event any further.这个特殊的返回值告诉 tkinter 停止进一步处理事件。

For example, to prevent a double-click from selecting the word under the cursor you could do this:例如,要防止双击选择光标下的单词,您可以执行以下操作:

text.bind("<Double-1>", lambda event: "break")

The downside to this approach is that you have to figure out what all of the bindings are that are related to the selection mechanism.这种方法的缺点是您必须弄清楚与选择机制相关的所有绑定是什么。 On the other hand, it gives you complete control over what each key or button press does.另一方面,它使您可以完全控制每个按键或按钮的作用。

A read-only, unselectable text widget.一个只读的、不可选择的文本小部件。

class Textarea(tkinter.Text):
  def __init__(self, master, **kw):
    super().__init__(master, **kw)
    # disable text alteration
    self.configure(state="disabled")
    # untag any selection from beginning to end
    def unselect(event):
      self.tag_remove("sel", "1.0", "end")
    # catch different ways selections could be made and unselect before copying or cutting
    good = ["<ButtonRelease-1>", "<Leave>", "<Control-c>", "<Control-C>", "<Control-x>", "<Control-X>"]
    better = good + ["<Shift-Left>", "<Shift-Up>", "<Shift-Right>", "<Shift-Down>", "<Shift-Home>", "<Shift-End>", "<Shift-Next>", "<Shift-Prior>"]
    excessive = better + ["<Shift-KP_1>", "<Shift-KP_2>", "<Shift-KP_3>", "<Shift-KP_4>", "<Shift-KP_6>", "<Shift-KP_7>", "<Shift-KP_8>", "<Shift-KP_9>"]
    for sequence in better:
      self.bind(sequence, unselect, add="+")
    # remove the appearance of selection
    self.configure(selectforeground=self.cget("foreground"), selectbackground=self.cget("background"))
    # disallow export of selection in case anything gets through
    self.configure(exportselection=False)

Tested on python 3.8.2在 python 3.8.2 上测试

I believe you will have to replace it with another widget that such as a Label or LabelFrame to accomplish this.我相信您必须用另一个小部件(例如LabelLabelFrame替换它才能完成此操作。 As well you could use a from tkinter import messagebox and have the text you want pop up in another window (like an info window or error message window).您也可以使用from tkinter import messagebox并在另一个窗口(如信息窗口或错误消息窗口)中弹出您想要的文本。 I think that as far as the Text widget goes, setting the state to disabled is the best you can do for your purposes unfortunately and users will be able to copy that text despite being unable to edit it.我认为就Text小部件而言,不幸的是,将状态设置为禁用是您可以为您的目的所做的最好的事情,尽管用户无法编辑它,但仍可以复制该文本。

Here is the simplest method to prevent text from being selected/highlighted when you just want the Text widget to be an ordinary log that is disabled and unselectable.当您只希望文本小部件是禁用且不可选择的普通日志时,这是防止选择/突出显示文本的最简单方法。

When I had the issue I figured I just needed to set some Text configuration property (highlightbackground, highlightcolor or selectbackground) to "Black".当我遇到这个问题时,我想我只需要将一些文本配置属性(highlightbackground、highlightcolor 或 selectbackground)设置为“黑色”。 Nothing worked.没有任何效果。 The Text widget employs tags that can be used to mark up the Text within the control. Text 小部件使用可用于标记控件内的 Text 的标签。 Configuration for user defined tags as well as the special tag "sel" have a number of settings including foreground (color) and background (color).用户定义标签以及特殊标签“sel”的配置有许多设置,包括前景(颜色)和背景(颜色)。

tag_config("sel", background="black") tag_config("sel", background="black")

Too easy right?太容易了吧? That doesn't work either.那也行不通。

Turns out that the highlight is actually a bitmap overlaid on the text.事实证明,突出显示实际上是覆盖在文本上的位图。 This is controlled by the bgstipple (bitmap) configuration for the tag.这是由标签的 bgstipple(位图)配置控制的。 The documentation indicates that there are a number of system bitmaps (shades of grey) that can be used however it is also possible to specify your own.该文档表明有许多系统位图(灰色阴影)可以使用,但也可以指定您自己的位图。 The bitmap needs to be an xbm and it's easy to create your own as it's a text file.位图必须是一个 xbm,而且很容易创建自己的位图,因为它是一个文本文件。

Put the following in a file named transparent.xbm.将以下内容放入名为 transparent.xbm 的文件中。

#define trans_width 2
#define trans_height 2
static unsigned char trans_bits[] = {
   0x00, 0x00
};

Here it is...这里是...

class TextLog(tk.Text):         
    def __init__(self, *args, **kwargs):  
        super().__init__(*args, **kwargs)
        self.tag_config("sel", bgstipple="@transparent.xbm", 
                                                       foreground="white")
        self.config(state="disabled")

    def write_log(self, text="", clear=False)
        self.configure(state='normal')

        if clear is True:
            self.delete("1.0","end")

        self.insert(tk.END, text)
        self.see(tk.END)
        self.config(state="disabled")

Depending on where the .xbm is relative to the module using the TextLog you may need to prefix it with a path "@path/to/transparent.xbm"根据 .xbm 相对于使用 TextLog 的模块的位置,您可能需要在它前面加上路径“@path/to/transparent.xbm”

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

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