import datetime as D
import fnmatch
import os
import pathlib
import stat
import string
import tkinter as T
import time
if os.name == 'posix':
import pwd, grp
HEIGHT = 32
FONT = ('HackGen Console', 12)
cwd = pathlib.Path('/')
tl = T.Tk() # toplevel window
tl.geometry('640x480')
tl.title('Preview')
buf = T.StringVar()
mask = T.StringVar()
font = FONT
wildcard = T.Entry( tl, font=font, bg='cyan', textvariable=mask)
filelist = T.Listbox(tl, font=font, bg='yellow', activestyle=T.NONE)
previewpain = T.Label( tl, font=font, bg='pink', textvariable=buf,
justify=T.LEFT, anchor=T.NW, height=HEIGHT, compound=T.CENTER)
def font_resize(size):
global font
if font[1] < 8 and size < 0:
return
font = (font[0], font[1] + size)
wildcard['font'] = font
filelist['font'] = font
previewpain['font'] = font
wildcard.pack(fill='both')
filelist.pack(fill='both')
previewpain.pack(fill='both')
def date5col(d):
if d < 0: return '[???]'
dx = D.datetime.fromtimestamp(d)
now = D.datetime.fromtimestamp(time.time())
delta = now - dx
if (delta < D.timedelta(days=1)):
d = dx.strftime('%H:%M')
elif (delta < D.timedelta(days=7)):
d = dx.strftime('%a%d')
elif (delta < D.timedelta(weeks=26)):
d = dx.strftime('%d%b')
else:
d = dx.strftime(' %Y')
return d
def size5col(n):
u = 'BKMGT'
v = 0
while n >= 1024:
n //= 1024
v += 1
if v >= len(u):
return 'LARGE'
return f'{n:4}{u[v]}'
def stat_str(k):
s = os.stat(k)
m = stat.filemode(s.st_mode)
n = s.st_nlink
u = s.st_uid
g = s.st_gid
d = date5col(s.st_ctime)
s = size5col(s.st_size)
i = k.name
t = f'{ m } { n } { u } { g } { s } { d } { i }\n'
return t
def fnmask(x):
l = mask.get().split()
if l == []: return True
for i in l:
i = i.strip()
# print(x, i)
if fnmatch.fnmatch(x, i):
return True
return False
def update_filelist():
global filenames
filenames = ['..']
filelist.delete(0, 'end')
filelist.insert(0, '..')
for i in os.listdir(cwd):
t = stat_str(cwd / i)
if not fnmask(i): continue
filelist.insert('end', t)
filenames.append(i)
#print(filenames)
def printable(x):
if x in string.whitespace:
return '.'
elif x in string.printable:
return x
else:
return '.'
def dump(data):
r = ''
for i in range(len(data) // 16 + 1):
d = data[:16]
t = ' '.join(map(lambda x: '%02x' % x, d))
a = ''.join(map(lambda x: printable(chr(x)), d))
r += f'{i * 16:8x} | {t:47} | { a }\n' # 47 = 16 * 3 - 1
data = data[16:]
return r
def read_file(file):
try:
with file.open() as fi:
t = fi.read(4096)
except UnicodeDecodeError:
with file.open('rb') as fi:
t = fi.read(4096)
except:
return f'<< FILE >>\nlen={ len(t) }'
if type(t) == bytes:
try:
t = str(t, errors='strict')
except UnicodeDecodeError:
pass
if type(t) == str:
return '\n'.join(t.split('\n')[:HEIGHT])
elif type(t) == bytes:
return f'<< FILE >>\nlen={ len(t) }\n{ dump(t) }'
def read_dir(the_dir):
t = ''
try:
for i in os.listdir(the_dir):
k = the_dir / i
t += stat_str(k)
except PermissionError:
return 'NO DATA'
return t
def update_preview(event=None):
t = filelist.curselection()
# print(f'cursel={t}')
if not t: return
n = t[0]
f = filelist.get(n)
f = filenames[n]
t = cwd / f
s = os.stat(t)
if stat.S_ISDIR(s.st_mode):
buf.set(f'{ t }\n<< DIR >>')
buf.set(f'{ t }\n{ read_dir(t) } ')
elif f[-4:] == '.png':
buf.set(f'{ t }\n<< FILE .png >>')
else:
buf.set(f'{ t }\n<< FILE >>')
buf.set(f'{ t }\n{ read_file(t) } ')
def change_dir(event=None):
global cwd
t = filelist.curselection()
# print(f'cursel={t}')
if not t: return
n = t[0]
f = filelist.get(n)
f = filenames[n]
t = cwd / f
s = os.stat(t)
if stat.S_ISDIR(s.st_mode):
# print(f'change_dir: { t }')
cwd = t.resolve()
update_filelist()
filelist.selection_set(0)
update_preview()
def change_dir_up(event=None):
filelist.selection_set(0)
change_dir()
def cursor_move(n, update=True):
global cwd
t = filelist.curselection()
# print(f'cursel={t} curmove={n}')
if not t: t = (0,)
n += t[0]
s = filelist.size()
if n < 0: n = 0
if n >= s: n = s - 1
filelist.selection_clear(0, 'end')
filelist.selection_set(n)
filelist.activate(n)
filelist.see(n)
if update:
update_preview()
update_filelist()
filelist.selection_set(0)
update_preview()
RET = '<Key-Return>'
BS = '<Key-BackSpace>'
LBS = '<<ListboxSelect>>'
filelist.bind(LBS, update_preview)
filelist.bind(RET, change_dir)
filelist.bind(BS, change_dir_up)
filelist.bind('l', change_dir)
filelist.bind('h', change_dir_up)
filelist.bind('J', lambda x: cursor_move(+1, update=False))
filelist.bind('K', lambda x: cursor_move(-1, update=False))
filelist.bind('j', lambda x: cursor_move(+1))
filelist.bind('k', lambda x: cursor_move(-1))
filelist.bind('u', lambda x: font_resize(+2))
filelist.bind('i', lambda x: font_resize(-2))
filelist.bind('r', lambda x: update_filelist())
filelist.bind('q', lambda x: tl.quit())
filelist.focus_set()
wildcard.bind('<KeyRelease>', lambda x: update_filelist())
tl.mainloop()
# vim:et:sw=4: