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

Tkinter开发HEX Viewer时同步滚动Text控件出现对齐偏移问题

Tkinter HEX Viewer多文本框同步滚动错位修复方案

问题复现

通过三个独立Text控件分别展示地址、十六进制值、ASCII字符,绑定了统一的滚动逻辑,但滚动一段时间后三列内容出现累计偏移,排查行高参数、字符一致性后问题仍存在。

原实现代码

import tkinter as tk
import os

def chunker(seq, size: int):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))

TAG_JUSTIFY_RIGHT = 'justify_right'

class Application(tk.Frame):
    BYTES_PER_ROW = 16

    def __init__(self, parent = None):
        tk.Frame.__init__(self, parent)
         
        self.textbox_address = tk.Text(self, width = 17, padx = 10, wrap = tk.NONE, bd = 0)
        self.textbox_address.pack(side = tk.LEFT, fill=tk.Y, expand = False)

        self.textbox_address.tag_configure(TAG_JUSTIFY_RIGHT, justify = tk.RIGHT)

        self.textbox_hex = tk.Text(self, width = 47, padx = 10, wrap = tk.NONE, bd = 0)
        self.textbox_hex.pack(side = tk.LEFT, fill = tk.Y, expand = False)

        self.textbox_ascii = tk.Text(self, width = 17, padx = 10, wrap = tk.NONE, bd = 0)
        self.textbox_ascii.pack(side = tk.LEFT, fill = tk.Y, expand = False)

        self.textboxes = [self.textbox_address, self.textbox_hex, self.textbox_ascii]

        self.scrollbar = tk.Scrollbar(self)
        self.scrollbar.pack(side = tk.RIGHT, fill = tk.Y, expand = False)

        self.scrollbar['command'] = self._on_scrollbar
        for textbox in self.textboxes:
            textbox['yscrollcommand'] = self._on_textscroll

        self.pack(fill = tk.BOTH, expand = True, side = tk.RIGHT)
     
    def _on_scrollbar(self, *args) -> None:
        for textbox in self.textboxes:
            textbox.yview(*args)

    def _on_textscroll(self, *args) -> None:
        self.scrollbar.set(*args)
        self._on_scrollbar('moveto', args[0])

    def _populate_address_area(self, num_bytes: int) -> None:
        num_lines = num_bytes // self.BYTES_PER_ROW
        chars_per_byte = 2
        format_pad_len = 8 * chars_per_byte

        for i in range(num_lines + 1):
            base_address = format(i * self.BYTES_PER_ROW, 'X').rjust(format_pad_len, '0')
            self.textbox_address.insert(tk.END, f"{base_address}\n")

        self.textbox_address.tag_add(TAG_JUSTIFY_RIGHT, 1.0, tk.END)
        self.textbox_address.config(state = tk.DISABLED)

    def populate_hex_view(self, byte_arr: bytes) -> None:
        self._populate_address_area(len(byte_arr))
        
        for chunk in chunker(byte_arr, self.BYTES_PER_ROW):
            hex_format = chunk.hex(" ")
            self.textbox_hex.insert(tk.END, f"{hex_format}\n")

            ascii_format = "".join([chr(i) if 32 <= i <= 127 else "." for i in chunk])
            self.textbox_ascii.insert(tk.END, f"{ascii_format}\n")

        self.textbox_hex.config(state = tk.DISABLED)
        self.textbox_ascii.config(state = tk.DISABLED)
 
if __name__ == "__main__":
    root = tk.Tk()
    root.title('Hex View')
    app = Application(parent = root)
    app.populate_hex_view(bytearray(os.urandom(0x4000)))
    root.mainloop()

问题现象

  • 初始状态对齐正常:
    初始对齐正常界面
  • 滚动后出现偏移:
    滚动后对齐偏移界面

修复方案

问题根源

三个Text控件未强制使用统一等宽字体,Tkinter默认字体渲染会因为每行的字符构成差异产生微小的行高误差,多次滚动后误差累计就会出现明显错位。

具体修改

在初始化Text控件时,给所有控件设置相同的等宽字体参数,同时统一设置spacing、padx等边距参数,确保每行的渲染高度完全一致。
修改__init__方法中的Text控件初始化代码如下:

# 统一设置等宽字体,可根据系统调整为Consolas、Monaco等其他等宽字体
FONT_CONFIG = ('Courier New', 10)

self.textbox_address = tk.Text(self, width = 17, padx = 10, wrap = tk.NONE, bd = 0, font=FONT_CONFIG, spacing1=0, spacing2=0, spacing3=0)
self.textbox_address.pack(side = tk.LEFT, fill=tk.Y, expand = False)

self.textbox_address.tag_configure(TAG_JUSTIFY_RIGHT, justify = tk.RIGHT)

self.textbox_hex = tk.Text(self, width = 47, padx = 10, wrap = tk.NONE, bd = 0, font=FONT_CONFIG, spacing1=0, spacing2=0, spacing3=0)
self.textbox_hex.pack(side = tk.LEFT, fill = tk.Y, expand = False)

self.textbox_ascii = tk.Text(self, width = 17, padx = 10, wrap = tk.NONE, bd = 0, font=FONT_CONFIG, spacing1=0, spacing2=0, spacing3=0)
self.textbox_ascii.pack(side = tk.LEFT, fill = tk.Y, expand = False)

如果仍有轻微偏移,可以进一步修改同步滚动逻辑,强制三个控件的行号完全对齐:

def _on_scrollbar(self, *args) -> None:
    # 先获取第一个控件的当前行位置,其他控件强制对齐到相同行
    first_visible = self.textbox_address.yview()[0]
    for textbox in self.textboxes:
        textbox.yview_moveto(first_visible)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 19:36:03