【问题标题】:tkinter label text with utf-8 color codes issue带有 utf-8 颜色代码的 tkinter 标签文本问题
【发布时间】:2020-12-13 19:50:00
【问题描述】:

我在 tkinter 标签中显示带有 utf-8 颜色代码的文本时遇到问题。 我的 python 脚本正在接收带有 utf-8 颜色代码的彩色单词的文本行,我将它们显示在 tk 标签上。虽然文本显示正确,但 utf-8 颜色代码不会为单词着色,而是显示为简单代码。

例子:

我们可以看到GPU=60.10C 应该是红色的,如下图所示:

如何在 tk 标签中显示颜色,而无需更改接收到的行并 grepping 颜色代码并将其作为文本颜色添加到标签中?

喜欢:

root = Tk()
Label(root,text="red text",fg="red").grid(column=0,row=0)

上述解决方案会比预期增加更多的复杂性和更多的实现!

【问题讨论】:

    标签: python-3.x tkinter


    【解决方案1】:

    在这里完成 Bryan Oakley 的回答是如何解析字符串并使用 Text 小部件中的标签应用颜色:

    1. 为您想要的颜色/格式代码配置标签

      text.tag_configure(<tag name>, **options)
      

      标签选项列表可用,例如here

    2. 创建一个字典,将每个颜色代码与相应的标签相关联

    3. Text 小部件中插入不带颜色代码的字符串。您可以使用regular expression 删除颜色代码:

      re.sub(r"\x1b\[((\d+;)*\d+)m", "", <string>)
      
    4. 使用相同的正则表达式,解析字符串以查找颜色代码并应用标签。这里有几个棘手的部分:

      • 颜色代码位置与文本索引不匹配,因为代码已从小部件中的文本中删除,因此您需要跟踪每行的累积偏移量。
      • 您需要等到找到“关闭”代码(例如颜色更改或\x1b[0m)才能添加带有text.tag_add(&lt;tag name&gt;, &lt;start&gt;, &lt;stop&gt;) 的标签,因此您需要跟踪打开的标签。李>

    代码示例,并不详尽(例如,不支持同时使用粗体和斜体),但涵盖了基础知识:

    import tkinter as tk
    import re
    
    root = tk.Tk()
    text = tk.Text(root)
    text.pack()
    
    # dictionaries to replace formatting code with tags
    ansi_font_format = {1: 'bold', 3: 'italic', 4: 'underline', 9: 'overstrike'}
    ansi_font_reset = {21: 'bold', 23: 'italic', 24: 'underline', 29: 'overstrike'}
    
    # tag configuration
    text.tag_configure('bold', font=('', 9, 'bold'))
    text.tag_configure('italic', font=('', 9, 'italic'))
    text.tag_configure('underline', underline=True)
    text.tag_configure('overstrike', overstrike=True)
    
    # dictionaries to replace color code with tags
    ansi_color_fg = {39: 'foreground default'}
    ansi_color_bg = {49: 'background default'}
    
    text.tag_configure('foreground default', foreground=text["fg"])
    text.tag_configure('background default', background=text["bg"])
    
    ansi_colors_dark = ['black', 'red', 'green', 'yellow', 'royal blue', 'magenta', 'cyan', 'light gray']
    ansi_colors_light = ['dark gray', 'tomato', 'light green', 'light goldenrod', 'light blue', 'pink', 'light cyan', 'white']
    
    for i, (col_dark, col_light) in enumerate(zip(ansi_colors_dark, ansi_colors_light)):
        ansi_color_fg[30 + i] = 'foreground ' + col_dark
        ansi_color_fg[90 + i] = 'foreground ' + col_light
        ansi_color_bg[40 + i] = 'background ' + col_dark
        ansi_color_bg[100 + i] = 'background ' + col_light
        # tag configuration
        text.tag_configure('foreground ' + col_dark, foreground=col_dark)
        text.tag_configure('background ' + col_dark, background=col_dark)
        text.tag_configure('foreground ' + col_light, foreground=col_light)
        text.tag_configure('background ' + col_light, background=col_light)
    
    # regular expressionto find ansi codes in string
    ansi_regexp = re.compile(r"\x1b\[((\d+;)*\d+)m")
    
    def insert_ansi(txt, index="insert"):
        first_line, first_char = map(int, str(text.index(index)).split("."))
        if index == "end":
            first_line -= 1
    
        lines = txt.splitlines()
        if not lines:
            return
        # insert text without ansi codes
        text.insert(index, ansi_regexp.sub('', txt))
        # find all ansi codes in txt and apply corresponding tags
        opened_tags = {}  # we need to keep track of the opened tags to be able to do
                          # text.tag_add(tag, start, end) when we reach a "closing" ansi code
    
        def apply_formatting(code, code_index):
            if code == 0:  # reset all by closing all opened tag
                for tag, start in opened_tags.items():
                    text.tag_add(tag, start, code_index)
                opened_tags.clear()
            elif code in ansi_font_format:  # open font formatting tag
                tag = ansi_font_format[code]
                opened_tags[tag] = code_index
            elif code in ansi_font_reset:   # close font formatting tag
                tag = ansi_font_reset[code]
                if tag in opened_tags:
                    text.tag_add(tag, opened_tags[tag], code_index)
                    opened_tags.remove(tag)
            elif code in ansi_color_fg:    # open foreground color tag (and close previously opened one if any)
                for tag in tuple(opened_tags):
                    if tag.startswith('foreground'):
                        text.tag_add(tag, opened_tags[tag], code_index)
                        opened_tags.remove(tag)
                opened_tags[ansi_color_fg[code]] = code_index
            elif code in ansi_color_bg:    # open background color tag (and close previously opened one if any)
                for tag in tuple(opened_tags):
                    if tag.startswith('background'):
                        text.tag_add(tag, opened_tags[tag], code_index)
                        opened_tags.remove(tag)
                opened_tags[ansi_color_bg[code]] = code_index
    
        def find_ansi(line_txt, line_nb, char_offset):
            delta = -char_offset  # difference between the character position in the original line and in the text widget
                                  # (initial offset due to insertion position if first line + extra offset due to deletion of ansi codes)
            for match in ansi_regexp.finditer(line_txt):
                codes = [int(c) for c in match.groups()[0].split(';')]
                start, end = match.span()
                for code in codes:
                    apply_formatting(code, "{}.{}".format(line_nb, start - delta))
                delta += end - start  # take into account offste due to deletion of ansi code
    
        find_ansi(lines[0], first_line, first_char) # first line, with initial offset due to insertion position
        for line_nb, line in enumerate(lines[1:], first_line + 1):
            find_ansi(line, line_nb, 0)   # next lines, no offset
        # close still opened tag
        for tag, start in opened_tags.items():
            text.tag_add(tag, start, "end")
    
    
    # example for the kind of output you can get with "ls --color"
    output = 'file.pdf\nfile.txt\n\x1b[0m\x1b[01;34mfolder\x1b[0m\n\x1b[01;32mscript.py\x1b[0m\ntest\n'
    insert_ansi(output, "end")
    

    Linux 中 ls --color 命令的彩色输出示例:

    【讨论】:

      【解决方案2】:

      Tkinter 小部件不支持嵌入在文本中的颜色代码。此外,Label 小部件只能有一个前景色和一个背景色。

      如果您想要多种颜色,唯一的选择是使用TextCanvas 小部件,解析字符串,并以适当的方式应用颜色。

      【讨论】:

        猜你喜欢
        • 2019-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-08
        • 1970-01-01
        • 1970-01-01
        • 2011-03-09
        相关资源
        最近更新 更多