I'm trying to implement a simple HEX Viewer by using three Text()
boxes which are set to scroll simultaneously.
However, it seems as though there's some kind of "drift" and at some point, the first box loses alignment with the other two. I can't seem to figure out why.
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()
At first, everything is aligned correctly:
However, after a while, there's a visible misalignment between the first column and the other two:
At first I thought that this might be related to line heights, but setting spacing1
, spacing2
, and spacing3
didn't help. The problem also persists if I replace all non-whitespace characters with a single character such as 'O' (to make sure all characters have the same height).
How can I ensure that all three boxes stay aligned?
CodePudding user response:
Inside _populate_address_area
, there is a for
loop: for i in range(num_lines 1):
. This is the cause of the problem. Using num_lines 1
adds one too many linse to textbox_address
. To fix this, there are two options: deleting the 1
, or using for i in range(1, num_lines 1):
. Either way, textbox_address
will have the correct number of lines.