You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter Text组件bgstipple加载自定义XBM时出现位置偏移异常

问题描述

运行下述Python脚本可生成仅包含单条竖线的XBM位图文件,支持将竖线放置在位图的左侧、居中、右侧位置。但将该自定义XBM文件赋值给Tkinter Text组件的bgstipple属性时,会出现固定异常:

  • 图案整体偏移半个字符宽度:XBM内竖线设为左对齐时,stipple效果中竖线显示在字符居中位置;XBM内竖线设为居中对齐时,stipple效果中竖线显示在字符末尾位置
  • 竖线设为右对齐时完全无法显示,且图案无预期的按字符折行环绕效果
    使用Tkinter内置XBM文件时不会触发该异常,下方代码可100%复现问题:
import tkinter as tk, tkinter.font as tkf
import math


class Text(tk.Text):
    def __init__(self, master, **kwargs):
        tk.Text.__init__(self, master, **dict(font='{Courier New} 14',wrap="none",exportselection=1,takefocus=1))
        self.make_caret_xbm('caret.xbm', self['font'])
        
    def make_caret_xbm(self, path, font, inswidth=1) -> None:
        #prepare
        f    = tkf.Font(font=font)
        h, w = f.metrics('linespace'), f.measure(' ')
        d, m = divmod(w, 8)
        bl   = d+(m>0)
        wtmz = f'{"0"*(bl*8)}'
        
        #line at beginning ~ appears in center of character
        lt = f'{"1"*inswidth}{wtmz}'
        
        #line in middle ~ appears at end of character
        cn = f'{"0"*(math.ceil(w/2))}{"1"*inswidth}{wtmz}'
        
        #line at end ~ does not appear
        rt = f'{"0"*(w-inswidth)}{"1"*inswidth}{wtmz}'
        
        #---set-line-type-here---vv
        row  = ','.join(hex(int( lt[(n:=int(i*8)):n+8] ,2)) for i in range(bl))
        
        xbmdata  = ',\n\t'.join(row for i in range(h))
        xbmdata  = (f"#define image_width {w}\n#define image_height {h}\n"
                    "static unsigned char image_bits[] = {\n\t"
                    f'{xbmdata}}};')
                    
        print(xbmdata)
        #save xbm
        with open(path, 'wb') as f: f.write(xbmdata.encode())
        
    def caret(self):
        #create the caret tag and add it to everything
        self.tag_configure('CARET',bgstipple="@caret.xbm",background='#FF0000')
        self.tag_add('CARET','1.0','end')


if __name__ == '__main__':
    class App(tk.Tk):
        def __init__(self, *args, **kwargs):
            tk.Tk.__init__(self, *args, **kwargs)
            #config cell
            self.columnconfigure(0, weight=1)
            self.rowconfigure   (0, weight=1)
            #instantiate editor
            (ed := Text(self)).grid(sticky='nswe')
            #insert text and apply caret to everything
            ed.insert('1.0', 'Hello World')
            ed.caret()
    #run        
    App().mainloop()
异常原因

异常由两个核心问题共同导致:

  1. XBM位序生成错误:XBM格式规定单字节存储的8个像素,遵循最低有效位(LSB)对应最左侧像素、最高有效位(MSB)对应最右侧像素的规则。原代码直接将从左到右排列的8位像素字符串转为整数,相当于把每个字节内的像素顺序完全反转,直接造成了半个字节(4像素,约半个等宽字符宽度)的固定偏移。
  2. 多余填充位+stipple平铺规则:Tk的stipple图案是从组件绘制原点开始全局平铺,不会自动按单个字符单元格对齐。原代码额外拼接了全0填充串,右对齐的竖线刚好落入XBM声明宽度外的填充区域,渲染时直接被裁剪,因此完全不显示。
修正方法

只需调整XBM生成逻辑,匹配XBM格式规范即可:

  • 每8位像素切片转换为整数前,先将切片字符串反转,符合LSB优先的存储规则
  • 移除多余的尾部全0填充逻辑,确保所有有效像素都落在XBM声明的宽度范围内

修正后的make_caret_xbm方法核心代码如下:

def make_caret_xbm(self, path, font, inswidth=1) -> None:
    f = tkf.Font(font=font)
    h, w = f.metrics('linespace'), f.measure(' ')
    # 计算每行需要的字节数,不足8位的部分补0凑整字节
    byte_per_row = (w + 7) // 8
    total_bit_per_row = byte_per_row * 8

    # 左对齐竖线
    lt = '1'*inswidth + '0'*(total_bit_per_row - inswidth)
    # 居中竖线
    offset = (w - inswidth)//2
    cn = '0'*offset + '1'*inswidth + '0'*(total_bit_per_row - offset - inswidth)
    # 右对齐竖线
    offset = w - inswidth
    rt = '0'*offset + '1'*inswidth + '0'*(total_bit_per_row - offset - inswidth)

    # 选择要生成的竖线位置,注意每8位反转后转整数,匹配XBM的LSB规则
    target_bits = lt
    row_bytes = []
    for i in range(byte_per_row):
        bit_seg = target_bits[i*8 : (i+1)*8]
        # 反转位顺序后转整数
        row_bytes.append(hex(int(bit_seg[::-1], 2)))
    row = ','.join(row_bytes)

    xbmdata = ',\n\t'.join(row for _ in range(h))
    xbmdata = (f"#define image_width {w}\n#define image_height {h}\n"
                "static unsigned char image_bits[] = {\n\t"
                f'{xbmdata}}};')
    with open(path, 'wb') as f:
        f.write(xbmdata.encode())

替换原有方法后,三个位置的竖线都可以正常显示,stipple图案也会按预期平铺。


内容的提问来源于stack exchange,提问作者OysterShucker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:36:15