如何在Tkinter Canvas上为文本添加多种字体样式
解决Tkinter Canvas中显示多格式文本的问题
Tkinter的Canvas组件本身不支持像Text那样的标签样式系统,要实现部分文本加粗/斜体,得手动解析带标签的输入文本,拆分不同样式的片段后逐个绘制。以下是实现方案:
核心思路
- 解析输入的带标签文本(比如
<b>、</b>、<i>、</i>),将文本拆分为「内容+对应字体样式」的片段列表 - 清除Canvas上旧的文本元素,逐个绘制每个片段,根据样式设置字体
- 保留原有的滚动逻辑,通过调整文本片段的Y坐标实现向上滚动
修改后的完整代码
import tkinter import tkinter.scrolledtext import re def parse_formatted_text(text): # 解析<b>、</b>、<i>、</i>标签,返回(文本内容, 字体样式)的列表 fragments = [] current_style = "normal" # 使用正则拆分标签和文本 parts = re.split(r'(<b>|</b>|<i>|</i>)', text) for part in parts: if part == '<b>': current_style = "bold" elif part == '</b>': current_style = "normal" elif part == '<i>': current_style = "italic" elif part == '</i>': current_style = "normal" elif part.strip(): # 跳过空内容 fragments.append((part, current_style)) return fragments def draw_formatted_text(canvas, x, y, fragments, base_font="Arial 10", width=280): # 清除旧的文本元素 for item in canvas.find_withtag("formatted_text"): canvas.delete(item) current_x = x current_y = y line_height = 14 # 根据字体调整行高 max_width = width for text, style in fragments: # 根据样式设置字体 if style == "bold": font = f"{base_font} bold" elif style == "italic": font = f"{base_font} italic" else: font = base_font # 处理文本换行(按宽度拆分) words = text.split() current_line = "" for word in words: test_line = f"{current_line} {word}".strip() # 计算当前测试行的宽度 bbox = canvas.create_text((0,0), text=test_line, font=font, anchor=tkinter.SW) line_width = canvas.bbox(bbox)[2] - canvas.bbox(bbox)[0] canvas.delete(bbox) if line_width <= max_width or not current_line: current_line = test_line else: # 绘制当前行 canvas.create_text((current_x, current_y), text=current_line, font=font, anchor=tkinter.SW, tag="formatted_text") current_y -= line_height current_line = word # 绘制剩余的当前行 if current_line: canvas.create_text((current_x, current_y), text=current_line, font=font, anchor=tkinter.SW, tag="formatted_text") current_y -= line_height return current_y def change_txtabs(event): abilities = txt_txtabs.get(1.0, tkinter.END).rstrip("\n") # 去掉末尾换行 fragments = parse_formatted_text(abilities) # 绘制格式化文本,锚点SW,从(12,432)开始 draw_formatted_text(main_pct, 12, 432, fragments, width=280) def scroll_text(): # 实现文本向上滚动,每100ms移动1像素 for item in main_pct.find_withtag("formatted_text"): x, y = main_pct.coords(item) main_pct.coords(item, x, y - 1) # 超出顶部的文本删除 for item in main_pct.find_withtag("formatted_text"): bbox = main_pct.bbox(item) if bbox and bbox[3] < 0: # 文本底部超出Canvas顶部 main_pct.delete(item) window.after(100, scroll_text) window = tkinter.Tk() window.geometry('930x565') txt_txtabs = tkinter.scrolledtext.ScrolledText(window, height=30, wrap='word', undo=True) txt_txtabs.bind("<KeyPress>", change_txtabs) txt_txtabs.bind("<KeyRelease>", change_txtabs) txt_txtabs.place(x=10,y=10,width=300) main_pct = tkinter.Canvas(bg='white', height=480, width=300) main_pct.place(x=350,y=10) # 启动滚动定时器 scroll_text() window.mainloop()
关键部分说明
- 文本解析函数:
parse_formatted_text用正则拆分标签和文本,跟踪当前样式,生成片段列表 - 绘制函数:
draw_formatted_text负责清除旧元素,按宽度拆分文本换行,逐个绘制不同样式的片段,用formatted_text标签统一管理所有文本元素 - 滚动逻辑:
scroll_text定时调整所有文本元素的Y坐标,超出Canvas顶部的元素自动删除,实现向上滚动效果
测试输入<b>This</b> word is <i>italic</i>.,Canvas会正确显示加粗的This和斜体的italic。
内容的提问来源于stack exchange,提问作者ToTaCe
相关产品推荐
相关产品推荐

