Christos TZOTZIOY Georgiou said:
Notice that Michael Peuser described an "extremely fast" "binary
editor"; I assume a dual view (hex / chars) one. This is not a text
widget --you don't have to account for line feeds and variable width
characters
Well I have somthing a little bit over 100 lines; I removed the editor part
but that is straight forward.
No scaling to size up to 1 GB.
-----------------------------------
# wckhwex by Michael Peuser May-2003
# Using some ideas from F. Lundh (WCK)
# This is Open Source. I will be happy if it could help somebody.
versionString="wckhex 0.3 by mpeuser.de"
from __future__ import division
from Tkinter import *
from WCK import Widget, EventMixin
from tkFileDialog import askopenfile
import mmap
class HexView(EventMixin, Widget):
"""Displays HUGE binary file using memory mapped files
"""
aCols=10 # address field
bCols=2.5*16+1 # binary field
cCols=16+1 # ascii field
ui_takefocus=1
ui_doublebuffer=0
def __init__(self,root, buffer=None):
self.buffer=buffer
self.defLines=(len(buffer)+15)//16
self.firstLine=0 # numer of first line in window
self.oldFirst=-1 # .. compared to last time
self.visLines=1 # visible lines in window
self.pix=None # pixmap will be created on resize
self.ui_init(root)
self.vscroll=Scrolls(self,orient=VERTICAL)
self.vscroll.pack(side=RIGHT,fill=Y)
self.pack(side=TOP,fill=BOTH,expand=1)
self.font=self.ui_font(0,"Courier 10")
(self.cWidth, self.lHeight) = self.font.measure("X")
def ui_handle_resize(self, width, height):
self.height=height
self.width= max(width,
(self.aCols+1+self.bCols+1+self.cCols)*self.cWidth) #
pixel
self.visLines=self.height//self.lHeight
self.pix=self.ui_pixmap(self.width, self.height)
self.vscroll.doScroll() # compute new thumb
self.drawPix()
def ui_handle_clear(*whatever):
pass # no need for clear as is pixmap long enough
def ui_handle_repair(self, draw, x0, y0, x1, y1):
#if self.pix:
draw.paste(self.pix)
def onclick(self, ev):
self.focus_set()
if ev.num==1:
pass #XXX implement editor here
if ev.num==3:
pop=Menu(self,tearoff=0);
pop.add_command(label=versionString,state=DISABLED)
pop.post(ev.x+self.winfo_rootx(),ev.y+self.winfo_rooty())
def onkey(self, event):
todo ={'Down': ("scroll",1,"lines"), 'Up': ('scroll',-1,'lines'),
'Next': ("scroll",1,"pages"), 'Prior'
'scroll',-1,'pages')}
x=todo.get(event.keysym)
print x
if x: self.vscroll.doScroll(*x)
def bgText(self,x,y,w,h,text,theBrush):
"Displays centered text with background color"
self.pix.rectangle((x+1,y+1,x+w-2,y+h-2),theBrush)
(tw,th)=self.pix.textsize(text,self.font)
self.pix.text((x+(w-tw)/2,y+(h-th)/2),text,self.font)
def drawPix(self,unchangedSize=0):
"Central dawing routine"
c=self.pix
repair=None
whiteBrush=self.ui_brush(0xffffff)
yellowBrush=self.ui_brush(0xffff99)
grayPen=self.ui_pen(0x999999,2)
ww,wh = self.ui_size()
h=self.lHeight
posX = 0
# speed up scrolling by image blitting, but unclean parametrisation of paste
if unchangedSize: ## check if blitting
useful
linesTBS = self.oldFirst-self.firstLine ## TBS = to be scrolled
if 0 < linesTBS < self.visLines/2: ## down, repair top
c.paste(c,(0, h*linesTBS))
theRange=range(1,linesTBS+1)
c.rectangle((0,h,ww,(linesTBS+1)*h),whiteBrush)
c.rectangle((0,h,self.aCols*self.cWidth,(linesTBS+1)*h),
yellowBrush)
elif 0 < -linesTBS < self.visLines/2: ## up, repair bottom
c.paste(c,(0, h*linesTBS))
delta=self.visLines+linesTBS
theRange=range(delta-1,self.visLines)
c.rectangle((0, delta*h, ww, wh),whiteBrush)
c.rectangle((0,delta*h, self.aCols*self.cWidth, wh),
yellowBrush)
else:
unchangedSize=0 # blitting would not make much
improvement...
self.oldFirst=self.firstLine # remember first line for
next time
# end of blitting optimization
if not unchangedSize: # all new
c.rectangle((0,0,ww,wh),whiteBrush)
theRange=range(1, self.visLines)
c.rectangle((0,h,self.aCols*self.cWidth,wh), yellowBrush)
for w, text in \
((self.aCols,"address"),(self.bCols,"hex"),(self.cCols,"ascii")):
w *= self.cWidth
if repair!='T':
self.bgText(posX, 0, w, h, text, yellowBrush)
c.line((posX - 1, 0, posX - 1, wh -1), grayPen)
posX+=w
# addresses and data
posY = self.lHeight
for y in theRange:
posX = 0
heightY = self.lHeight
address=(y-1+self.firstLine)*16
vals = self.buffer[address:address+16]
c.text((posX,y*self.lHeight),
"%3x.%05x " % divmod(address, 1024*1024), self.font)
posX += self.aCols*self.cWidth
for vala in range(address,address+16):
if vala %8==0: posX+=self.cWidth//2 # some in between
space
if vala<len(buffer):
c.text((posX,
y*heightY),"%02x"%ord(buffer[vala]),self.font)
posX += 2.5*self.cWidth
c.text((posX+10,y*heightY),buffer[address:address+16],self.font)
self.ui_damage()
self.focus_set()
class Scrolls(Scrollbar):
"wrapper around Tk scrolbars"
def __init__(self,frame,orient=None,**kw):
Scrollbar.__init__(self,frame,orient=orient,command=self.doScroll)
self.visible=1 # start packed
self.theMaster=frame
def doScroll(self,a=None,b=None,c=None):
m=self.theMaster
oldfirst=m.firstLine
if a=="scroll":
if c=='pages': b=int(b)*m.visLines-1
m.firstLine = oldfirst+int(b)
elif a=="moveto":
m.firstLine=int(float(b)*(m.defLines+1))
if m.firstLine+m.visLines>m.defLines: # the end
m.firstLine=m.defLines-m.visLines+1
if m.firstLine<0:
m.firstLine=0
size = m.visLines/m.defLines # 0...1
start= m.firstLine/m.defLines # 0...1
if self.visible: self.set(start,start+size)
if oldfirst != m.firstLine: m.drawPix(unchangedSize=1)
if __name__ == "__main__":
root=Tk()
root.iconify()
fbb=askopenfile("r+"); assert fbb, "No file"
buffer=mmap.mmap(fbb.fileno(),0)
s=HexView(root, buffer=buffer)
root.title("%s (%6.3f MB)" %(fbb.name, len(buffer)/1024/1024))
root.geometry("720x550+10+10")
root.deiconify()
root.mainloop()
fbb.close()
# the End