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