Compare commits
4 Commits
e6f09eb5f2
...
012c27255f
Author | SHA1 | Date |
---|---|---|
multiSnow | 012c27255f | |
multiSnow | 4f57cb763f | |
multiSnow | f9a9dde09c | |
multiSnow | ce44436091 |
|
@ -69,6 +69,8 @@ def validate(conf):
|
||||||
# user_interface
|
# user_interface
|
||||||
assert isinstance(conf.user_interface.cache,int),\
|
assert isinstance(conf.user_interface.cache,int),\
|
||||||
f'wrong type for user_interface.cache: {user_interface.cache!r}'
|
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,\
|
assert conf.user_interface.cache>-1,\
|
||||||
f'user_interface.cache should not be negative: {conf.user_interface.cache}'
|
f'user_interface.cache should not be negative: {conf.user_interface.cache}'
|
||||||
assert isinstance(conf.user_interface.usepage,bool),\
|
assert isinstance(conf.user_interface.usepage,bool),\
|
||||||
|
|
|
@ -41,6 +41,8 @@ apng = true
|
||||||
[user_interface]
|
[user_interface]
|
||||||
# amount of decoded image to keep as cache, 0 to disable cache
|
# amount of decoded image to keep as cache, 0 to disable cache
|
||||||
cache = 0
|
cache = 0
|
||||||
|
# whether using numeric sort (('a1', 'a10', 'a2') => ('a1', 'a2', 'a10'))
|
||||||
|
numsort = false
|
||||||
# start with showing page
|
# start with showing page
|
||||||
usepage = false
|
usepage = false
|
||||||
# start with showing double image in canvas mode
|
# start with showing double image in canvas mode
|
||||||
|
|
|
@ -184,12 +184,12 @@ def _writelog(epoch,ns,pid,ismt,level,levelcolor,target,usecolor,buf,blank):
|
||||||
|
|
||||||
def _recvfrompipe(pipe,append,args):
|
def _recvfrompipe(pipe,append,args):
|
||||||
try:
|
try:
|
||||||
if not (data:=pipe.recv_bytes()):
|
if (data:=pipe.recv_bytes())==b'stop':
|
||||||
pipe.send_bytes(b'')
|
pipe.send_bytes(b'stop')
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('pipe failed',file=stderr)
|
# print('pipe failed',file=stderr)
|
||||||
print(''.join(format_exception(e)),file=stderr)
|
# print(''.join(format_exception(e)),file=stderr)
|
||||||
return True
|
return True
|
||||||
epoch,ns,pid,ismt,error=unpack(LOGHDRFMT,data[:LOGHDRFMTLEN])
|
epoch,ns,pid,ismt,error=unpack(LOGHDRFMT,data[:LOGHDRFMTLEN])
|
||||||
logfnerr,logfnerr_usecolor,logfnout,logfnout_usecolor=args
|
logfnerr,logfnerr_usecolor,logfnout,logfnout_usecolor=args
|
||||||
|
@ -237,7 +237,14 @@ def _logloop(pipes,config,*,interval=1):
|
||||||
sort=buf.sort
|
sort=buf.sort
|
||||||
clear=buf.clear
|
clear=buf.clear
|
||||||
while pipes:
|
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
|
continue
|
||||||
for pipe in conns:
|
for pipe in conns:
|
||||||
while pipe.poll():
|
while pipe.poll():
|
||||||
|
@ -272,9 +279,12 @@ def log(modulename,*args,exc=None,devel=False,level='log',levelcolor=None,error=
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def logend():
|
def logend():
|
||||||
_pipe.send_bytes(b'')
|
with _pipe:
|
||||||
_pipe.recv_bytes()
|
_pipe.send_bytes(b'stop')
|
||||||
|
_pipe.recv_bytes()
|
||||||
if _locker is not None:
|
if _locker is not None:
|
||||||
|
for recver in _recvers:
|
||||||
|
recver.close()
|
||||||
with _locker:pass
|
with _locker:pass
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,14 @@ from .constants import progname
|
||||||
__all__=[
|
__all__=[
|
||||||
'str2bytes','bytes2str','packstr','unpackstr',
|
'str2bytes','bytes2str','packstr','unpackstr',
|
||||||
'prettybytes','prettypath','prettycolor','prettyrect',
|
'prettybytes','prettypath','prettycolor','prettyrect',
|
||||||
|
'sorter_numeric',
|
||||||
]
|
]
|
||||||
|
|
||||||
_LENFMT='<Q'
|
_LENFMT='<Q'
|
||||||
_LENLEN=calcsize(_LENFMT)
|
_LENLEN=calcsize(_LENFMT)
|
||||||
|
|
||||||
_printable=tuple(ord(c) for c in f'{ascii_letters}{digits}')
|
_printable=tuple(ord(c) for c in f'{ascii_letters}{digits}')
|
||||||
|
_nums=f'{digits}.'
|
||||||
|
|
||||||
def str2bytes(s):
|
def str2bytes(s):
|
||||||
return s.encode('utf8') if isinstance(s,str) else s
|
return s.encode('utf8') if isinstance(s,str) else s
|
||||||
|
@ -65,6 +67,39 @@ def prettycolor(color):
|
||||||
def prettyrect(rect):
|
def prettyrect(rect):
|
||||||
return '{2}x{3}+{0}+{1}'.format(*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():
|
def _logger_init():
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from os import environ
|
from os import environ
|
||||||
|
|
|
@ -17,6 +17,13 @@ from ..co import OrderedDict
|
||||||
|
|
||||||
__all__=['Containers']
|
__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):
|
class Containers(OrderedDict):
|
||||||
|
|
||||||
__slots__=(
|
__slots__=(
|
||||||
|
@ -42,13 +49,6 @@ class Containers(OrderedDict):
|
||||||
filenames.sort(key=self.sortkey,reverse=self.revsort)
|
filenames.sort(key=self.sortkey,reverse=self.revsort)
|
||||||
self._fnpos=self[container].index(filename)
|
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
|
@property
|
||||||
def sortkey(self):
|
def sortkey(self):
|
||||||
# 'key' argument that used in sorting filenames
|
# 'key' argument that used in sorting filenames
|
||||||
|
@ -106,13 +106,12 @@ class Containers(OrderedDict):
|
||||||
assert isinstance(step,int)
|
assert isinstance(step,int)
|
||||||
if not step:return
|
if not step:return
|
||||||
oldpos=self._fnpos
|
oldpos=self._fnpos
|
||||||
while True:
|
try:
|
||||||
try:
|
filenames=self.getvalue(self._pos)
|
||||||
self._fnpos=self._relocate(self.getvalue(self._pos),self._fnpos,step)
|
except IndexError:
|
||||||
break
|
return self.cross(-1)
|
||||||
except IndexError:
|
else:
|
||||||
if not self.cross(-1):
|
self._fnpos=_relocate(filenames,self._fnpos,step)
|
||||||
return False
|
|
||||||
return self._fnpos!=oldpos
|
return self._fnpos!=oldpos
|
||||||
|
|
||||||
def cross(self,step=1,/):
|
def cross(self,step=1,/):
|
||||||
|
|
|
@ -67,7 +67,8 @@ class ENV(NonWidget):
|
||||||
self._thumbnails={}
|
self._thumbnails={}
|
||||||
# {container:[pathname, ...], ...}
|
# {container:[pathname, ...], ...}
|
||||||
# any method on _containers should be locked by _containerlock
|
# 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), ...]
|
# [(target,args,kwds), ...]
|
||||||
# any method on _actions should be in mainthread
|
# any method on _actions should be in mainthread
|
||||||
self._actions=[]
|
self._actions=[]
|
||||||
|
@ -517,7 +518,8 @@ class ENV(NonWidget):
|
||||||
path=unpackstr(data)
|
path=unpackstr(data)
|
||||||
scene_id,loop_count=args
|
scene_id,loop_count=args
|
||||||
if scene_id!=self.get_scene_id() or not self.is_in_scene(path):
|
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)
|
self._animes.discard(path)
|
||||||
return
|
return
|
||||||
if (canvas:=self.getcanvas(path)) is None:
|
if (canvas:=self.getcanvas(path)) is None:
|
||||||
|
@ -531,8 +533,9 @@ class ENV(NonWidget):
|
||||||
self._animes.discard(path)
|
self._animes.discard(path)
|
||||||
return
|
return
|
||||||
self.runactions()
|
self.runactions()
|
||||||
start_new_thread(self.update_anime,
|
start_new_thread(
|
||||||
(path,scene_id,loop_count,canvas.current_delay()))
|
self.update_anime,
|
||||||
|
(path,scene_id,loop_count,canvas.current_delay()))
|
||||||
case SDLE_EvEmu.SDL_SDLUI_MOTIONCONTINUE:
|
case SDLE_EvEmu.SDL_SDLUI_MOTIONCONTINUE:
|
||||||
x,y,motion_id=args
|
x,y,motion_id=args
|
||||||
if motion_id!=self._motion_id:
|
if motion_id!=self._motion_id:
|
||||||
|
@ -542,7 +545,8 @@ class ENV(NonWidget):
|
||||||
assert verb(__name__,f'{self}: motion interrupted at edge')
|
assert verb(__name__,f'{self}: motion interrupted at edge')
|
||||||
self._motion_id=None
|
self._motion_id=None
|
||||||
else:
|
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:
|
case SDLE_EvEmu.SDL_TEXTINPUT|SDLE_EvEmu.SDL_TEXTEDITING:
|
||||||
pass # ignore
|
pass # ignore
|
||||||
case _ as evtype:
|
case _ as evtype:
|
||||||
|
@ -712,7 +716,8 @@ class ENV(NonWidget):
|
||||||
self._inrequire.add(('thumbnail',key))
|
self._inrequire.add(('thumbnail',key))
|
||||||
if canvas:
|
if canvas:
|
||||||
self._inrequire.add(('canvas',key))
|
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,/):
|
def cancelrequire(self,key,is_canvas=True,/):
|
||||||
with self._inrequirelock:
|
with self._inrequirelock:
|
||||||
if not self._inrequire:
|
if not self._inrequire:
|
||||||
|
|
Loading…
Reference in New Issue