突出显示 canvas 中的部分文本项

Highlight parts of text items in a canvas

我想用一些文本项填充 canvas 小部件并突出显示这些文本的一部分(考虑在 canvas 的所有文本项中突出显示搜索词组)。

当然,可以将单个文本拆分为突出显示和非突出显示部分,并为这些部分添加单独的文本项,例如

#!/usr/bin/env wish

proc _drawText {canvas x y text highlighted} {
    if {$text != {}} {
        set cmd [list $canvas create text $x $y -anchor nw -text $text]
        if {$highlighted == "1"} {
            lappend cmd -fill red
        }
        set t [{*}$cmd]
        # advance x to the bbox' right side
        set x [lindex [$canvas bbox $t] 2]
        # adjust x, because the bbox seems to be 1 pixel too wide :(
        incr x -1
    }
    return $x
}

proc addText {canvas x y text {highlightedChars {}}} {
    set highlighted "0"
    set chars ""
    foreach c [split $text {}] h [split $highlightedChars {}] {
        if {$h != $highlighted} {           
            set x [_drawText $canvas $x $y $chars $highlighted]
            set highlighted $h
            set chars ""
        }
        append chars $c
    }
    _drawText $canvas $x $y $chars $highlighted
}

canvas .c
pack .c -expand 1 -fill both

# add "some example text" and highlight the two occurrences of "ex"
addText .c 0 0 \
    "some example text" \
    "00000110000000110"

这是一种非常有限的方法(-> 仅适用于 nw 锚定文本,换行必须手动实现,等等),所以我想知道是否有更好的方法来做到这一点。有什么想法吗?

首先,文本小部件可能更适合您的问题如所述,因为它支持更复杂的文本标记。画布仅支持单个项目的单个选定范围,听起来它们不会为您正在做的更复杂的事情提供很多支持。画布与文本小部件同样复杂,但将它们的复杂性集中在其他地方;要在它们上显示复杂的文本,您必须编写大量脚本。

但是,您可以很容易地找出文本的位置。首先,(只要文本没有旋转)你可以忽略锚点和锚点控制,只使用整个边界框。其次,要处理多行文本,您确实需要知道对齐规则,但这实际上只是说明如何将每一行水平放置在边界框的子部分内。

第三,由于 canvas 个文本项具有单一字体,您可以在 font measurefont metrics 的帮助下轻松地进行文本计算。 font measure 命令可以让您确定一段文本在特定字体中的宽度,而无需在屏幕上显示它(前提是文本中没有换行符)。 font metrics 命令可以让您找出 -linespace 是什么字体,即行与行之间应该有多少 space,以及 -ascent-descent 是(行的顶部和底部相对于文本的基线)。这些命令调用与 Tk 实际显示文本完全相同的字体引擎;您不需要硬编码任何关于您正在使用的字体的大小和形状的知识。

令人惊讶的是,没有将项目索引转换为 canvas 坐标对的简单机制。