tw.view: new classes: scroll_action, sort_action, display_action

This commit is contained in:
The MMGen Project 2022-12-11 12:12:07 +00:00
commit eb04c84f26
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
12 changed files with 134 additions and 95 deletions

View file

@ -1 +1 @@
13.3.dev29
13.3.dev30

View file

@ -33,17 +33,16 @@ Actions: [q]uit menu, r[e]draw, add [l]abel:
'a':'s_amt',
'A':'s_age',
'M':'s_twmmid',
'r':'d_reverse',
'r':'s_reverse',
'D':'d_days',
'e':'d_redraw',
'E':'d_showempty',
'u':'d_showused',
'L':'d_all_labels',
'q':'a_quit',
'v':'a_view',
'w':'a_view_detail',
'p':'a_print_detail',
'l':'a_comment_add' }
'l':'i_comment_add' }
async def get_rpc_data(self):

View file

@ -28,13 +28,12 @@ Pruning: [q]uit pruning, [p]rune, [u]nprune, [c]lear prune list:
'a':'s_amt',
'A':'s_age',
'M':'s_twmmid',
'r':'d_reverse',
'r':'s_reverse',
'D':'d_days',
'e':'d_redraw',
'E':'d_showempty',
'U':'d_showused',
'L':'d_all_labels',
'q':'a_quit',
'v':'a_view',
'w':'a_view_detail',
'p':'a_prune',

View file

@ -242,13 +242,12 @@ Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw:
'a':'s_amt',
'm':'s_total_amt',
't':'s_txid',
'r':'d_reverse',
'r':'s_reverse',
'D':'d_days',
'e':'d_redraw',
'u':'d_show_unconfirmed',
'i':'d_show_txid',
'T':'d_show_total_amt',
'q':'a_quit',
'v':'a_view',
'V':'a_view_detail',
'p':'a_print_squeezed',

View file

@ -40,17 +40,16 @@ Actions: [q]uit menu, [p]rint, r[e]draw, add [l]abel:
'a':'s_amt',
'd':'s_addr',
'A':'s_age',
'r':'d_reverse',
'M':'s_twmmid',
'r':'s_reverse',
'D':'d_days',
'o':'d_group',
'm':'d_mmid',
'e':'d_redraw',
'q':'a_quit',
'p':'a_print_detail',
'v':'a_view',
'w':'a_view_detail',
'l':'a_comment_add' }
'l':'i_comment_add' }
async def get_rpc_data(self):
# bitcoin-cli help listunspent:

View file

@ -30,13 +30,12 @@ Actions: [q]uit menu, r[e]draw, [D]elete addr, add [l]abel:
key_mappings = {
'a':'s_amt',
'M':'s_twmmid',
'r':'d_reverse',
'r':'s_reverse',
'e':'d_redraw',
'E':'d_showempty',
'L':'d_all_labels',
'q':'a_quit',
'l':'a_comment_add',
'D':'a_addr_delete',
'l':'i_comment_add',
'D':'i_addr_delete',
'v':'a_view',
'w':'a_view_detail',
'p':'a_print_detail' }

View file

@ -54,17 +54,16 @@ Actions: [q]uit menu, [D]elete addr, add [l]abel, [R]efresh balance:
key_mappings = {
'a':'s_amt',
'd':'s_addr',
'r':'d_reverse',
'r':'s_reverse',
'M':'s_twmmid',
'm':'d_mmid',
'e':'d_redraw',
'q':'a_quit',
'p':'a_print_detail',
'v':'a_view',
'w':'a_view_detail',
'l':'a_comment_add',
'D':'a_addr_delete',
'R':'a_balance_refresh' }
'l':'i_comment_add',
'D':'i_addr_delete',
'R':'i_balance_refresh' }
no_data_errmsg = 'No accounts in tracking wallet!'

View file

@ -362,10 +362,7 @@ class TwAddresses(TwView):
elif False in res:
return False
class action(TwView.action):
def s_amt(self,parent):
parent.do_sort('amt')
class display_action(TwView.display_action):
def d_showempty(self,parent):
parent.showempty = not parent.showempty

View file

@ -119,7 +119,7 @@ class TwAddressesPrune(TwAddresses):
else:
msg('\nInvalid keypress')
def a_prune(self,parent):
async def a_prune(self,parent):
def do_entry(desc,n,addrnum,e):
if auto[desc]:
@ -160,13 +160,13 @@ class TwAddressesPrune(TwAddresses):
if parent.scroll:
msg_r(CUR_HOME + ERASE_ALL)
def a_unprune(self,parent):
async def a_unprune(self,parent):
for addrnum in self.get_addrnums(parent,'unprune'):
parent.disp_data[addrnum-1].tag = False
if parent.scroll:
msg_r(CUR_HOME + ERASE_ALL)
def a_clear_prune_list(self,parent):
async def a_clear_prune_list(self,parent):
for d in parent.data:
d.tag = False

View file

@ -182,7 +182,10 @@ class TwTxHistory(TwView):
def dump_fn_pfx(self):
return 'transaction-history' + (f'-since-block-{self.sinceblock}' if self.sinceblock else '')
class action(TwView.action):
class sort_action(TwView.sort_action):
def s_blockheight(self,parent):
parent.do_sort('blockheight')
def s_amt(self,parent):
parent.do_sort('amt')
@ -192,6 +195,8 @@ class TwTxHistory(TwView):
parent.do_sort('total_amt')
parent.show_total_amt = True
class display_action(TwView.display_action):
def d_show_txid(self,parent):
parent.show_txid = not parent.show_txid

View file

@ -226,12 +226,14 @@ class TwUnspentOutputs(TwView):
o.date = dates[idx]
self.dates_set = True
class action(TwView.action):
class sort_action(TwView.sort_action):
def s_twmmid(self,parent):
parent.do_sort('twmmid')
parent.show_mmid = True
class display_action(TwView.display_action):
def d_mmid(self,parent):
parent.show_mmid = not parent.show_mmid

View file

@ -38,6 +38,17 @@ CUR_UP = lambda n: f'\033[{n}A'
CUR_DOWN = lambda n: f'\033[{n}B'
ERASE_ALL = '\033[0J'
# decorator for action.run():
def enable_echo(orig_func):
async def f(self,parent,action_method):
if parent.scroll:
parent.term.set('echo')
ret = await orig_func(self,parent,action_method)
if parent.scroll:
parent.term.set('noecho')
return ret
return f
# base class for TwUnspentOutputs,TwAddresses,TwTxHistory:
class TwView(MMGenObject,metaclass=AsyncInit):
@ -495,6 +506,14 @@ class TwView(MMGenObject,metaclass=AsyncInit):
async def view_filter_and_sort(self):
action_map = {
'a_': 'action',
's_': 'sort_action',
'd_': 'display_action',
'm_': 'scroll_action',
'i_': 'item_action',
}
def make_key_mappings(scroll):
if scroll:
for k in self.scroll_keys['vi']:
@ -506,6 +525,8 @@ class TwView(MMGenObject,metaclass=AsyncInit):
scroll = self.scroll = g.scroll
key_mappings = make_key_mappings(scroll)
action_classes = { k: getattr(self,action_map[v[:2]])() for k,v in key_mappings.items() }
action_methods = { k: getattr(v,key_mappings[k]) for k,v in action_classes.items() }
prompt = self.prompt_fs.strip().format(
s='\nScrolling: k=up, j=down, b=pgup, f=pgdown, g=top, G=bottom' if scroll else '' )
@ -516,10 +537,12 @@ class TwView(MMGenObject,metaclass=AsyncInit):
clear_screen = '\n\n' if opt.no_blank else CUR_HOME + ('' if scroll else ERASE_ALL)
from ..term import get_term,get_char,get_char_raw
if scroll:
term = get_term()
term.register_cleanup()
term.set('noecho')
self.term = get_term()
self.term.register_cleanup()
self.term.set('noecho')
get_char = get_char_raw
msg_r(CUR_HOME + ERASE_ALL)
@ -540,31 +563,18 @@ class TwView(MMGenObject,metaclass=AsyncInit):
self.oneshot_msg = ''
if reply not in key_mappings:
if not scroll:
msg_r('\ninvalid keypress ')
await asyncio.sleep(0.3)
continue
action = key_mappings[reply]
if scroll and action.startswith('a_'): # 'a_' actions may require line input
term.set('echo')
if hasattr(self.action,action):
if action.startswith('m_'): # scrolling actions
self.use_cached = True
await self.action().run(self,action)
elif action.startswith('s_'): # put here to allow overriding by action method
self.do_sort(action[2:])
elif hasattr(self.item_action,action):
await self.item_action().run(self,action)
elif action == 'a_quit':
if reply in key_mappings:
ret = action_classes[reply].run(self,action_methods[reply])
if type(ret).__name__ == 'coroutine':
await ret
elif reply == 'q':
msg('')
if self.scroll:
self.term.set('echo')
return self.disp_data
if scroll and action.startswith('a_'):
term.set('noecho')
elif not scroll:
msg_r('\ninvalid keypress ')
await asyncio.sleep(0.3)
@property
def blank_prompt(self):
@ -581,23 +591,9 @@ class TwView(MMGenObject,metaclass=AsyncInit):
class action:
async def run(self,parent,action):
ret = getattr(self,action)(parent)
if type(ret).__name__ == 'coroutine':
await ret
def d_days(self,parent):
af = parent.age_fmts
parent.age_fmt = af[(af.index(parent.age_fmt) + 1) % len(af)]
if parent.update_widths_on_age_toggle: # TODO
pass
def d_redraw(self,parent):
msg_r(CUR_HOME + ERASE_ALL)
def d_reverse(self,parent):
parent.data.reverse()
parent.reverse = not parent.reverse
@enable_echo
async def run(self,parent,action_method):
return await action_method(parent)
async def a_print_detail(self,parent):
return await self._print(parent,output_type='detail')
@ -647,27 +643,10 @@ class TwView(MMGenObject,metaclass=AsyncInit):
msg_r(CUR_HOME)
do_pager( await parent.format('detail',color=True) )
async def m_cursor_up(self,parent):
parent.pos -= min( parent.pos - 0, 1 )
async def m_cursor_down(self,parent):
parent.pos += min( parent.max_pos - parent.pos, 1 )
async def m_pg_up(self,parent):
parent.pos -= min( parent.scrollable_height, parent.pos - 0 )
async def m_pg_down(self,parent):
parent.pos += min( parent.scrollable_height, parent.max_pos - parent.pos )
async def m_top(self,parent):
parent.pos = 0
async def m_bot(self,parent):
parent.pos = parent.max_pos
class item_action:
async def run(self,parent,action):
@enable_echo
async def run(self,parent,action_method):
if not parent.disp_data:
return
@ -695,7 +674,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
# None: action aborted by user or no action performed
# False: an error occurred
# 'redo': user will be re-prompted for item number
ret = await getattr(self,action)(parent,idx)
ret = await action_method(parent,idx)
if ret != 'redo':
break
await asyncio.sleep(0.5)
@ -706,7 +685,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
CUR_HOME + ERASE_ALL +
await parent.format(display_type='squeezed',interactive=True,scroll=True) )
async def a_balance_refresh(self,parent,idx):
async def i_balance_refresh(self,parent,idx):
if not parent.keypress_confirm(
f'Refreshing tracking wallet {parent.item_desc} #{idx}. Is this what you want?'):
return 'redo'
@ -714,7 +693,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
await parent.get_data()
parent.oneshot_msg = yellow(f'{parent.proto.dcoin} balance for account #{idx} refreshed')
async def a_addr_delete(self,parent,idx):
async def i_addr_delete(self,parent,idx):
if not parent.keypress_confirm(
'Removing {} {} from tracking wallet. Is this what you want?'.format(
parent.item_desc, red(f'#{idx}') )):
@ -728,7 +707,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
parent.oneshot_msg = red('Address could not be removed')
return False
async def a_comment_add(self,parent,idx):
async def i_comment_add(self,parent,idx):
async def do_comment_add(comment):
@ -766,3 +745,65 @@ class TwView(MMGenObject,metaclass=AsyncInit):
return 'redo'
return await do_comment_add(res)
class scroll_action:
def run(self,parent,action_method):
self.use_cached = True
return action_method(parent)
def m_cursor_up(self,parent):
parent.pos -= min( parent.pos - 0, 1 )
def m_cursor_down(self,parent):
parent.pos += min( parent.max_pos - parent.pos, 1 )
def m_pg_up(self,parent):
parent.pos -= min( parent.scrollable_height, parent.pos - 0 )
def m_pg_down(self,parent):
parent.pos += min( parent.scrollable_height, parent.max_pos - parent.pos )
def m_top(self,parent):
parent.pos = 0
def m_bot(self,parent):
parent.pos = parent.max_pos
class sort_action:
def run(self,parent,action_method):
return action_method(parent)
def s_addr(self,parent):
parent.do_sort('addr')
def s_age(self,parent):
parent.do_sort('age')
def s_amt(self,parent):
parent.do_sort('amt')
def s_txid(self,parent):
parent.do_sort('txid')
def s_twmmid(self,parent):
parent.do_sort('twmmid')
def s_reverse(self,parent):
parent.data.reverse()
parent.reverse = not parent.reverse
class display_action:
def run(self,parent,action_method):
return action_method(parent)
def d_days(self,parent):
af = parent.age_fmts
parent.age_fmt = af[(af.index(parent.age_fmt) + 1) % len(af)]
if parent.update_widths_on_age_toggle: # TODO
pass
def d_redraw(self,parent):
msg_r(CUR_HOME + ERASE_ALL)