Compare commits

...

4 Commits

Author SHA1 Message Date
multiSnow 012c27255f
option for numeric sort 2022-12-18 16:57:34 +08:00
multiSnow 4f57cb763f
remove closed logger pipe 2022-12-18 15:04:35 +08:00
multiSnow f9a9dde09c
code style 2022-12-18 14:54:20 +08:00
multiSnow ce44436091
start from first page if previous container is empty 2022-12-18 14:46:35 +08:00
6 changed files with 80 additions and 27 deletions

View File

@ -69,6 +69,8 @@ def validate(conf):
# user_interface
assert isinstance(conf.user_interface.cache,int),\
f'wrong type for user_interface.cache: {user_interface.cache!r}'
assert isinstance(conf.user_interface.numsort,bool),\
f'wrong type for user_interface.numsort: {conf.user_interface.numsort!r}'
assert conf.user_interface.cache>-1,\
f'user_interface.cache should not be negative: {conf.user_interface.cache}'
assert isinstance(conf.user_interface.usepage,bool),\

View File

@ -41,6 +41,8 @@ apng = true
[user_interface]
# amount of decoded image to keep as cache, 0 to disable cache
cache = 0
# whether using numeric sort (('a1', 'a10', 'a2') => ('a1', 'a2', 'a10'))
numsort = false
# start with showing page
usepage = false
# start with showing double image in canvas mode

View File

@ -184,12 +184,12 @@ def _writelog(epoch,ns,pid,ismt,level,levelcolor,target,usecolor,buf,blank):
def _recvfrompipe(pipe,append,args):
try:
if not (data:=pipe.recv_bytes()):
pipe.send_bytes(b'')
if (data:=pipe.recv_bytes())==b'stop':
pipe.send_bytes(b'stop')
return True
except Exception as e:
print('pipe failed',file=stderr)
print(''.join(format_exception(e)),file=stderr)
# print('pipe failed',file=stderr)
# print(''.join(format_exception(e)),file=stderr)
return True
epoch,ns,pid,ismt,error=unpack(LOGHDRFMT,data[:LOGHDRFMTLEN])
logfnerr,logfnerr_usecolor,logfnout,logfnout_usecolor=args
@ -237,7 +237,14 @@ def _logloop(pipes,config,*,interval=1):
sort=buf.sort
clear=buf.clear
while pipes:
if not (conns:=waitpipe(pipes,timeout=interval)):
try:
if not (conns:=waitpipe(pipes,timeout=interval)):continue
except Exception as exc:
# print(f'wait failed {exc!r}',file=stderr)
# remove closed pipe
closed_pipes=set(pipe for pipe in pipes if pipe.closed)
pipes-=closed_pipes
closed_pipes.clear()
continue
for pipe in conns:
while pipe.poll():
@ -272,9 +279,12 @@ def log(modulename,*args,exc=None,devel=False,level='log',levelcolor=None,error=
return True
def logend():
_pipe.send_bytes(b'')
_pipe.recv_bytes()
with _pipe:
_pipe.send_bytes(b'stop')
_pipe.recv_bytes()
if _locker is not None:
for recver in _recvers:
recver.close()
with _locker:pass
return True

View File

@ -22,12 +22,14 @@ from .constants import progname
__all__=[
'str2bytes','bytes2str','packstr','unpackstr',
'prettybytes','prettypath','prettycolor','prettyrect',
'sorter_numeric',
]
_LENFMT='<Q'
_LENLEN=calcsize(_LENFMT)
_printable=tuple(ord(c) for c in f'{ascii_letters}{digits}')
_nums=f'{digits}.'
def str2bytes(s):
return s.encode('utf8') if isinstance(s,str) else s
@ -65,6 +67,39 @@ def prettycolor(color):
def prettyrect(rect):
return '{2}x{3}+{0}+{1}'.format(*rect)
def sorter_numeric(s):
parts=s.split('/')
results=[]
b,_,e=parts[-1].rpartition('.')
if b and e.strip(_nums):
parts[-1:]=[b,e]
else:
parts.append('')
while parts[:-1]:
results.append([])
part=parts.pop(0)
while part.endswith('.'):
part=part[:-1]
parts[0]=f'.{parts[0]}'
while part:
try:
start=min(part.index(s) for s in digits if s in part)
except ValueError:
results[-1].append((part,0))
break
head,part=part[:start],part[start:]
if head:
results[-1].append((head,0))
tail=part.lstrip(_nums)
nstr,part=part[:len(part)-len(tail)],tail
while nstr.endswith('.'):
nstr=nstr[:-1]
part=f'.{part}'
results[-1].append(('',float(nstr)))
results[-1].append(('',0))
results.append([(parts.pop(),0)])
return results
def _logger_init():
from functools import partial
from os import environ

View File

@ -17,6 +17,13 @@ from ..co import OrderedDict
__all__=['Containers']
def _relocate(lst,pos,step):
# change from current position with step from pos in lst, return new position
assert isinstance(step,int)
if not (lst and step):return 0
pos+=step
return min(max(0,pos),len(lst)-1)
class Containers(OrderedDict):
__slots__=(
@ -42,13 +49,6 @@ class Containers(OrderedDict):
filenames.sort(key=self.sortkey,reverse=self.revsort)
self._fnpos=self[container].index(filename)
def _relocate(self,lst,pos,step):
# change from current position with step from pos in lst, return new position
assert isinstance(step,int)
if not step:return
pos+=step
return min(max(0,pos),len(lst)-1)
@property
def sortkey(self):
# 'key' argument that used in sorting filenames
@ -106,13 +106,12 @@ class Containers(OrderedDict):
assert isinstance(step,int)
if not step:return
oldpos=self._fnpos
while True:
try:
self._fnpos=self._relocate(self.getvalue(self._pos),self._fnpos,step)
break
except IndexError:
if not self.cross(-1):
return False
try:
filenames=self.getvalue(self._pos)
except IndexError:
return self.cross(-1)
else:
self._fnpos=_relocate(filenames,self._fnpos,step)
return self._fnpos!=oldpos
def cross(self,step=1,/):

View File

@ -67,7 +67,8 @@ class ENV(NonWidget):
self._thumbnails={}
# {container:[pathname, ...], ...}
# any method on _containers should be locked by _containerlock
self._containers=Containers()
self._containers=Containers(
key=sorter_numeric if config.user_interface.numsort else None)
# [(target,args,kwds), ...]
# any method on _actions should be in mainthread
self._actions=[]
@ -517,7 +518,8 @@ class ENV(NonWidget):
path=unpackstr(data)
scene_id,loop_count=args
if scene_id!=self.get_scene_id() or not self.is_in_scene(path):
assert note(__name__,f'{self}: anime stop',prettypath(*path))
assert note(__name__,f'{self}: anime stop',
prettypath(*path))
self._animes.discard(path)
return
if (canvas:=self.getcanvas(path)) is None:
@ -531,8 +533,9 @@ class ENV(NonWidget):
self._animes.discard(path)
return
self.runactions()
start_new_thread(self.update_anime,
(path,scene_id,loop_count,canvas.current_delay()))
start_new_thread(
self.update_anime,
(path,scene_id,loop_count,canvas.current_delay()))
case SDLE_EvEmu.SDL_SDLUI_MOTIONCONTINUE:
x,y,motion_id=args
if motion_id!=self._motion_id:
@ -542,7 +545,8 @@ class ENV(NonWidget):
assert verb(__name__,f'{self}: motion interrupted at edge')
self._motion_id=None
else:
self.puttask(SDL_SDLUI,SDL_SDLUI_MOTIONCONTINUE,(x,y,motion_id))
self.puttask(SDL_SDLUI,SDL_SDLUI_MOTIONCONTINUE,
(x,y,motion_id))
case SDLE_EvEmu.SDL_TEXTINPUT|SDLE_EvEmu.SDL_TEXTEDITING:
pass # ignore
case _ as evtype:
@ -712,7 +716,8 @@ class ENV(NonWidget):
self._inrequire.add(('thumbnail',key))
if canvas:
self._inrequire.add(('canvas',key))
self.listener.speak('load' if canvas else 'thum',*key,b'T' if cutin else b'')
self.listener.speak('load' if canvas else 'thum',*key,
b'T' if cutin else b'')
def cancelrequire(self,key,is_canvas=True,/):
with self._inrequirelock:
if not self._inrequire: