Browse Source

tw.common,tw.unspent: various changes and cleanups

The MMGen Project 2 years ago
parent
commit
d4cae73cf0

+ 19 - 6
mmgen/base_proto/bitcoin/tw/unspent.py

@@ -36,13 +36,26 @@ Display options: toggle [D]ays/date, show [g]roup, show [m]mgen addr, r[e]draw
 Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
 """
 	key_mappings = {
-		't':'s_txid','a':'s_amt','d':'s_addr','A':'s_age','r':'d_reverse','M':'s_twmmid',
-		'D':'d_days','g':'d_group','m':'d_mmid','e':'d_redraw',
-		'q':'a_quit','p':'a_print','v':'a_view','w':'a_view_wide','l':'a_lbl_add' }
+		't':'s_txid',
+		'a':'s_amt',
+		'd':'s_addr',
+		'A':'s_age',
+		'r':'d_reverse',
+		'M':'s_twmmid',
+		'D':'d_days',
+		'g':'d_group',
+		'm':'d_mmid',
+		'e':'d_redraw',
+		'q':'a_quit',
+		'p':'a_print_detail',
+		'v':'a_view',
+		'w':'a_view_detail',
+		'l':'a_lbl_add' }
+
 	col_adj = 38
-	display_fs_fs     = ' {{n:{cw}}} {{t:{tw}}} {{v:2}} {{a}} {{A}} {{c:<}}'
-	display_hdr_fs_fs = ' {{n:{cw}}} {{t:{tw}}} {{a}} {{A}} {{c:<}}'
-	print_fs_fs       = ' {{n:4}} {{t:{tw}}} {{a}} {{m}} {{A:{aw}}} {cf}{{b:<8}} {{D:<19}} {{l}}'
+	squeezed_fs_fs     = ' {{n:{cw}}} {{t:{tw}}} {{v:2}} {{a}} {{A}} {{c:<}}'
+	squeezed_hdr_fs_fs = ' {{n:{cw}}} {{t:{tw}}} {{a}} {{A}} {{c:<}}'
+	wide_fs_fs         = ' {{n:4}} {{t:{tw}}} {{a}} {{m}} {{A:{aw}}} {cf}{{b:<8}} {{D:<19}} {{l}}'
 
 	async def get_rpc_data(self):
 		# bitcoin-cli help listunspent:

+ 18 - 10
mmgen/base_proto/ethereum/tw/unspent.py

@@ -44,13 +44,22 @@ Actions:         [q]uit view, [p]rint to file, pager [v]iew, [w]ide view,
                  add [l]abel, [D]elete address, [R]efresh balance:
 """
 	key_mappings = {
-		'a':'s_amt','d':'s_addr','r':'d_reverse','M':'s_twmmid',
-		'm':'d_mmid','e':'d_redraw',
-		'q':'a_quit','p':'a_print','v':'a_view','w':'a_view_wide',
-		'l':'a_lbl_add','D':'a_addr_delete','R':'a_balance_refresh' }
-	display_fs_fs = ' {{n:{cw}}} {{a}} {{A}}'
-	print_fs_fs   = ' {{n:4}} {{a}} {{m}} {{A:{aw}}} {{l}}'
-	display_hdr_fs_fs = display_fs_fs
+		'a':'s_amt',
+		'd':'s_addr',
+		'r':'d_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_lbl_add',
+		'D':'a_addr_delete',
+		'R':'a_balance_refresh' }
+
+	squeezed_fs_fs = squeezed_hdr_fs_fs = ' {{n:{cw}}} {{a}} {{A}}'
+	wide_fs_fs = ' {{n:4}} {{a}} {{m}} {{A:{aw}}} {{l}}'
 	no_data_errmsg = 'No accounts in tracking wallet!'
 
 	async def __init__(self,proto,*args,**kwargs):
@@ -82,9 +91,8 @@ class EthereumTokenTwUnspentOutputs(EthereumTwUnspentOutputs):
 
 	prompt_fs = 'Total to spend: {} {}\n\n'
 	col_adj = 37
-	display_fs_fs = ' {{n:{cw}}} {{a}} {{A}} {{A2}}'
-	print_fs_fs   = ' {{n:4}} {{a}} {{m}} {{A:{aw}}} {{A2:{aw}}} {{l}}'
-	display_hdr_fs_fs = display_fs_fs
+	squeezed_fs_fs = squeezed_hdr_fs_fs = ' {{n:{cw}}} {{a}} {{A}} {{A2}}'
+	wide_fs_fs = ' {{n:4}} {{a}} {{m}} {{A:{aw}}} {{A2:{aw}}} {{l}}'
 
 	async def __init__(self,proto,*args,**kwargs):
 		await super().__init__(proto,*args,**kwargs)

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-13.1.0
+13.2.dev1

+ 7 - 8
mmgen/tool/rpc.py

@@ -97,7 +97,7 @@ class tool_cmd(tool_cmd_base):
 		return await al.format( showbtcaddrs, sort, show_age, age_fmt or 'confs' )
 
 	async def twops(self,
-			obj,pager,reverse,wide,sort,age_fmt,show_mmid,wide_show_confs,interactive):
+			obj,pager,reverse,detail,sort,age_fmt,interactive,show_mmid):
 
 		obj.interactive = interactive
 		obj.reverse = reverse
@@ -109,10 +109,10 @@ class tool_cmd(tool_cmd_base):
 		if interactive:
 			await obj.view_and_sort()
 			return True
-		elif wide:
-			return await obj.format_for_printing( color=True, show_confs=wide_show_confs )
+		elif detail:
+			return await obj.format_detail( color=True )
 		else:
-			return await obj.format_for_display()
+			return await obj.format_squeezed()
 
 	async def twview(self,
 			pager           = False,
@@ -121,15 +121,14 @@ class tool_cmd(tool_cmd_base):
 			minconf         = 1,
 			sort            = 'age',
 			age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs',
-			show_mmid       = True,
-			wide_show_confs = True,
-			interactive     = False ):
+			interactive     = False,
+			show_mmid       = True ):
 		"view tracking wallet unspent outputs"
 
 		from ..tw.unspent import TwUnspentOutputs
 		obj = await TwUnspentOutputs(self.proto,minconf=minconf)
 		ret = await self.twops(
-			obj,pager,reverse,wide,sort,age_fmt,show_mmid,wide_show_confs,interactive)
+			obj,pager,reverse,wide,sort,age_fmt,interactive,show_mmid)
 		del obj.wallet
 		return ret
 

+ 116 - 85
mmgen/tw/common.py

@@ -25,15 +25,13 @@ import sys,time
 from ..globalvars import g
 from ..objmethods import Hilite,InitErrors,MMGenObject
 from ..obj import TwComment,get_obj,MMGenIdx,MMGenList
-from ..color import nocolor,red,yellow,green
+from ..color import nocolor,yellow,green
 from ..util import msg,msg_r,fmt,die,line_input,do_pager,capfirst,make_timestr
 from ..addr import MMGenID
 
 # mixin class for TwUnspentOutputs,TwAddrList:
 class TwCommon:
 
-	fmt_display = ''
-	fmt_print   = ''
 	cols        = None
 	reverse     = False
 	group       = False
@@ -45,6 +43,14 @@ class TwCommon:
 	age_fmts_interactive = ('confs','block','days','date')
 	_age_fmt = 'confs'
 
+	age_col_params = {
+		'confs':     (7,  'Confs'),
+		'block':     (8,  'Block'),
+		'days':      (6,  'Age(d)'),
+		'date':      (8,  'Date'),
+		'date_time': (16, 'Date/Time'),
+	}
+
 	date_formatter = {
 		'days': lambda rpc,secs: (rpc.cur_date - secs) // 86400 if secs else 0,
 		'date': (
@@ -72,7 +78,8 @@ class TwCommon:
 
 		lbl_id = ('account','label')['label_api' in self.rpc.caps]
 
-		self.data = MMGenList(self.gen_data(rpc_data,lbl_id))
+		res = self.gen_data(rpc_data,lbl_id)
+		self.data = MMGenList(await res if type(res).__name__ == 'coroutine' else res)
 
 		if not self.data:
 			die(1,self.no_data_errmsg)
@@ -88,6 +95,14 @@ class TwCommon:
 			for idx,o in enumerate(us):
 				o.date = dates[idx]
 
+	@property
+	def age_w(self):
+		return self.age_col_params[self.age_fmt][0]
+
+	@property
+	def age_hdr(self):
+		return self.age_col_params[self.age_fmt][1]
+
 	@property
 	def age_fmt(self):
 		return self._age_fmt
@@ -124,9 +139,17 @@ class TwCommon:
 			else:
 				return min_cols
 
+	sort_disp = {
+		'addr':   'Addr',
+		'age':    'Age',
+		'amt':    'Amt',
+		'txid':   'TxID',
+		'twmmid': 'MMGenID',
+	}
+
 	def sort_info(self,include_group=True):
 		ret = ([],['Reverse'])[self.reverse]
-		ret.append(capfirst(self.sort_key).replace('Twmmid','MMGenID'))
+		ret.append(self.sort_disp[self.sort_key])
 		if include_group and self.group and (self.sort_key in ('addr','txid','twmmid')):
 			ret.append('Grouped')
 		return ret
@@ -147,49 +170,54 @@ class TwCommon:
 		assert type(reverse) == bool
 		self.data.sort(key=self.sort_funcs[key],reverse=reverse or self.reverse)
 
-	async def format_for_display(self):
-		data = self.data
-		if self.has_age and self.age_fmt in self.age_fmts_date_dependent:
-			await self.set_dates(self.rpc,data)
-
-		if not getattr(self,'column_params',None):
-			self.set_column_params()
-
-		if self.group and (self.sort_key in ('addr','txid','twmmid')):
-			for a,b in [(data[i],data[i+1]) for i in range(len(data)-1)]:
-				for k in ('addr','txid','twmmid'):
-					if self.sort_key == k and getattr(a,k) == getattr(b,k):
-						b.skip = (k,'addr')[k=='twmmid']
-
-		self.fmt_display = (
-			self.hdr_fmt.format(
-				a = ' '.join(self.sort_info()),
-				b = self.proto.dcoin,
-				c = self.total.hl() if hasattr(self,'total') else None )
-			+ ('\nChain: '+green(self.proto.chain_name.upper()) if self.proto.chain_name != 'mainnet' else '')
-			+ '\n' + '\n'.join(self.gen_display_output(self.column_params))
-			+ '\n'
-		)
-
-		return self.fmt_display
-
-	async def format_for_printing(self,color=False,show_confs=True):
+	async def format_squeezed(self,color=True,cached=False):
+
+		if not cached:
+			data = self.data
+			if self.has_age and self.age_fmt in self.age_fmts_date_dependent:
+				await self.set_dates(self.rpc,data)
+
+			if not getattr(self,'column_params',None):
+				self.set_column_params()
+
+			if self.group and (self.sort_key in ('addr','txid','twmmid')):
+				for a,b in [(data[i],data[i+1]) for i in range(len(data)-1)]:
+					for k in ('addr','txid','twmmid'):
+						if self.sort_key == k and getattr(a,k) == getattr(b,k):
+							b.skip = (k,'addr')[k=='twmmid']
+
+			self._format_squeezed_display_data = (
+				self.hdr_fmt.format(
+					a = ' '.join(self.sort_info()),
+					b = self.proto.dcoin,
+					c = self.total.hl() if hasattr(self,'total') else None )
+				+ '\nNetwork: {}'.format((nocolor,green)[color](
+					self.proto.coin + ' ' +
+					self.proto.chain_name.upper() ))
+				+ '\n' + '\n'.join(self.gen_squeezed_display(self.column_params,color=color))
+				+ '\n'
+			)
+
+		return self._format_squeezed_display_data
+
+	async def format_detail(self,color):
 		if self.has_age:
 			await self.set_dates(self.rpc,self.data)
 
-		self.fmt_print = self.print_hdr_fs.format(
+		sep = self.detail_display_separator
+
+		return self.print_hdr_fs.format(
 			a = capfirst(self.desc),
 			b = self.rpc.blockcount,
 			c = make_timestr(self.rpc.cur_date),
-			d = ('' if self.proto.chain_name == 'mainnet' else
-				'Chain: {}\n'.format((nocolor,green)[color](self.proto.chain_name.upper())) ),
+			d = 'Network: {}\n'.format((nocolor,green)[color](
+				self.proto.coin + ' ' +
+				self.proto.chain_name.upper() )),
 			e = ' '.join(self.sort_info(include_group=False)),
-			f = '\n'.join(self.gen_print_output(color,show_confs)),
+			f = sep.join(self.gen_detail_display(color)),
 			g = self.proto.dcoin,
 			h = self.total.hl(color=color) if hasattr(self,'total') else None )
 
-		return self.fmt_print
-
 	async def view_and_sort(self):
 		from ..opts import opt
 		from ..term import get_char
@@ -201,10 +229,10 @@ class TwCommon:
 		ERASE_ALL = '\033[0J'
 
 		while True:
-			msg_r('' if self.no_output else '\n\n' if opt.no_blank else CUR_HOME+ERASE_ALL)
+			msg_r('' if self.no_output else '\n\n' if (opt.no_blank or g.test_suite) else CUR_HOME+ERASE_ALL)
 			reply = get_char(
 				'' if self.no_output else (
-					await self.format_for_display()
+					await self.format_squeezed()
 					+ '\n'
 					+ (self.oneshot_msg or '')
 					+ self.prompt
@@ -219,67 +247,73 @@ class TwCommon:
 				continue
 
 			action = self.key_mappings[reply]
-			if action.startswith('s_'):
+			if hasattr(self.action,action):
+				await self.action().run(self,action)
+			elif action.startswith('s_'): # put here to allow overriding by action method
 				self.do_sort(action[2:])
-				if action == 's_twmmid':
-					self.show_mmid = True
-			elif action == 'd_days':
-				af = self.age_fmts_interactive
-				self.age_fmt = af[(af.index(self.age_fmt) + 1) % len(af)]
-				if self.update_params_on_age_toggle:
-					self.set_column_params()
-			elif action == 'd_mmid':
-				self.show_mmid = not self.show_mmid
-			elif action == 'd_group':
-				if self.can_group:
-					self.group = not self.group
-			elif action == 'd_redraw':
+			elif hasattr(self.item_action,action):
+				await self.item_action().run(self,action)
 				self.set_column_params()
-			elif action == 'd_reverse':
-				self.data.reverse()
-				self.reverse = not self.reverse
 			elif action == 'a_quit':
 				msg('')
 				return self.data
-			elif hasattr(self.action,action):
-				await self.action(self).run(action)
-			elif hasattr(self.item_action,action):
-				await self.item_action(self).run(action)
-				self.set_column_params()
 
 	class action:
 
-		def __init__(self,parent):
-			self.parent = parent
+		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_interactive
+			parent.age_fmt = af[(af.index(parent.age_fmt) + 1) % len(af)]
+			if parent.update_params_on_age_toggle:
+				parent.set_column_params()
+
+		def d_redraw(self,parent):
+			parent.set_column_params()
 
-		async def run(self,action):
-			await getattr(self,action)(self.parent)
+		def d_reverse(self,parent):
+			parent.data.reverse()
+			parent.reverse = not parent.reverse
 
-		async def a_print(self,parent):
-			outfile = '{}-{}{}[{}].out'.format(
+		async def a_print_detail(self,parent):
+			return await self._print(parent,output_type='detail')
+
+		async def a_print_squeezed(self,parent):
+			return await self._print(parent,output_type='squeezed')
+
+		async def _print(self,parent,output_type):
+			outfile = '{}{}-{}{}[{}].out'.format(
 				parent.dump_fn_pfx,
+				f'-{output_type}' if len(parent.print_output_types) > 1 else '',
 				parent.proto.dcoin,
 				('' if parent.proto.network == 'mainnet' else '-'+parent.proto.network.upper()),
-				','.join(parent.sort_info(include_group=False)).lower() )
+				','.join(parent.sort_info(include_group=False)).replace(' ','') )
 			msg('')
 			from ..fileutil import write_data_to_file
 			from ..exception import UserNonConfirmation
+			hdr = {
+				'squeezed': f'[screen print truncated to width {parent.cols}]\n',
+				'detail': '',
+			}[output_type]
 			try:
 				write_data_to_file(
-					outfile,
-					await parent.format_for_printing(color=False),
-					desc = f'{parent.desc} listing' )
+					outfile = outfile,
+					data    = hdr + await getattr(parent,f'format_{output_type}')(color=False),
+					desc    = f'{parent.desc} listing' )
 			except UserNonConfirmation as e:
-				parent.oneshot_msg = red(f'File {outfile!r} not overwritten by user request\n\n')
+				parent.oneshot_msg = yellow(f'File {outfile!r} not overwritten by user request\n\n')
 			else:
-				parent.oneshot_msg = yellow(f'Data written to {outfile!r}\n\n')
+				parent.oneshot_msg = green(f'Data written to {outfile!r}\n\n')
 
 		async def a_view(self,parent):
-			do_pager(parent.fmt_display)
+			do_pager( await parent.format_squeezed(color=True,cached=True) )
 			self.post_view(parent)
 
-		async def a_view_wide(self,parent):
-			do_pager( await parent.format_for_printing(color=True) )
+		async def a_view_detail(self,parent):
+			do_pager( await parent.format_detail(color=True) )
 			self.post_view(parent)
 
 		def post_view(self,parent):
@@ -290,19 +324,16 @@ class TwCommon:
 
 	class item_action:
 
-		def __init__(self,parent):
-			self.parent = parent
-
-		async def run(self,action):
+		async def run(self,parent,action):
 			msg('')
 			while True:
-				ret = line_input(f'Enter {self.parent.item_desc} number (or RETURN to return to main menu): ')
+				ret = line_input(f'Enter {parent.item_desc} number (or RETURN to return to main menu): ')
 				if ret == '':
 					return None
 				idx = get_obj(MMGenIdx,n=ret,silent=True)
-				if not idx or idx < 1 or idx > len(self.parent.data):
-					msg(f'Choice must be a single number between 1 and {len(self.parent.data)}')
-				elif (await getattr(self,action)(self.parent,idx)) != 'redo':
+				if not idx or idx < 1 or idx > len(parent.data):
+					msg(f'Choice must be a single number between 1 and {len(parent.data)}')
+				elif (await getattr(self,action)(parent,idx)) != 'redo':
 					break
 
 class TwMMGenID(str,Hilite,InitErrors,MMGenObject):

+ 34 - 22
mmgen/tw/unspent.py

@@ -54,6 +54,8 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 		watch-only wallet using 'mmgen-addrimport' and then re-run this program.
 	"""
 	update_params_on_age_toggle = False
+	detail_display_separator = '\n'
+	print_output_types = ('detail',)
 
 	class MMGenTwUnspentOutput(MMGenListItem):
 		txid         = ListItemAttr(CoinTxID)
@@ -136,23 +138,16 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 			['col1_w','mmid_w','addr_w','btaddr_w','label_w','tx_w','txdots']
 			)(col1_w,  mmid_w,  addr_w,  btaddr_w,  label_w,  tx_w,  txdots)
 
-	def gen_display_output(self,c):
-		fs     = self.display_fs_fs.format(     cw=c.col1_w, tw=c.tx_w )
-		hdr_fs = self.display_hdr_fs_fs.format( cw=c.col1_w, tw=c.tx_w )
+	def gen_squeezed_display(self,c,color):
+		fs     = self.squeezed_fs_fs.format(     cw=c.col1_w, tw=c.tx_w )
+		hdr_fs = self.squeezed_hdr_fs_fs.format( cw=c.col1_w, tw=c.tx_w )
 		yield hdr_fs.format(
 			n  = 'Num',
 			t  = 'TXid'.ljust(c.tx_w - 2) + ' Vout',
 			a  = 'Address'.ljust(c.addr_w),
 			A  = f'Amt({self.proto.dcoin})'.ljust(self.disp_prec+5),
 			A2 = f' Amt({self.proto.coin})'.ljust(self.disp_prec+4),
-			c  = {
-					'confs':     'Confs',
-					'block':     'Block',
-					'days':      'Age(d)',
-					'date':      'Date',
-					'date_time': 'Date',
-				}[self.age_fmt],
-			).rstrip()
+			c  = self.age_hdr ).rstrip()
 
 		for n,i in enumerate(self.data):
 			addr_dots = '|' + '.'*(c.addr_w-1)
@@ -163,20 +158,20 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 					f'Non-{g.proj_name}'
 				),
 				width = c.mmid_w,
-				color = True )
+				color = color )
 
 			if self.show_mmid:
 				addr_out = '{} {}{}'.format((
-					type(i.addr).fmtc(addr_dots,width=c.btaddr_w,color=True) if i.skip == 'addr' else
-					i.addr.fmt(width=c.btaddr_w,color=True)
+					type(i.addr).fmtc(addr_dots,width=c.btaddr_w,color=color) if i.skip == 'addr' else
+					i.addr.fmt(width=c.btaddr_w,color=color)
 				),
 					mmid_disp,
-					(' ' + i.label.fmt(width=c.label_w,color=True)) if c.label_w > 0 else ''
+					(' ' + i.label.fmt(width=c.label_w,color=color)) if c.label_w > 0 else ''
 				)
 			else:
 				addr_out = (
-					type(i.addr).fmtc(addr_dots,width=c.addr_w,color=True) if i.skip=='addr' else
-					i.addr.fmt(width=c.addr_w,color=True) )
+					type(i.addr).fmtc(addr_dots,width=c.addr_w,color=color) if i.skip=='addr' else
+					i.addr.fmt(width=c.addr_w,color=color) )
 
 			yield fs.format(
 				n  = str(n+1)+')',
@@ -186,18 +181,21 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 					i.txid[:c.tx_w-len(c.txdots)] + c.txdots ),
 				v  = i.vout,
 				a  = addr_out,
-				A  = i.amt.fmt(color=True,prec=self.disp_prec),
-				A2 = (i.amt2.fmt(color=True,prec=self.disp_prec) if i.amt2 is not None else ''),
+				A  = i.amt.fmt(color=color,prec=self.disp_prec),
+				A2 = (i.amt2.fmt(color=color,prec=self.disp_prec) if i.amt2 is not None else ''),
 				c  = self.age_disp(i,self.age_fmt),
 				).rstrip()
 
-	def gen_print_output(self,color,show_confs):
+	def gen_detail_display(self,color):
+
 		addr_w = max(len(i.addr) for i in self.data)
 		mmid_w = max(len(('',i.twmmid)[i.twmmid.type=='mmgen']) for i in self.data) or 12 # DEADBEEF:S:1
-		fs = self.print_fs_fs.format(
+
+		fs = self.wide_fs_fs.format(
 			tw = self.txid_w + 3,
-			cf = '{c:<8} ' if show_confs else '',
+			cf = '{c:<8} ',
 			aw = self.proto.coin_amt.max_prec + 5 )
+
 		yield fs.format(
 			n  = 'Num',
 			t  = 'Tx ID,Vout',
@@ -211,6 +209,7 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 			l  = 'Label' )
 
 		max_lbl_len = max([len(i.label) for i in self.data if i.label] or [2])
+
 		for n,i in enumerate(self.data):
 			yield fs.format(
 				n  = str(n+1) + ')',
@@ -244,6 +243,19 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 			len(self.data),
 			suf(self.data) ))
 
+	class action(TwCommon.action):
+
+		def s_twmmid(self,parent):
+			parent.do_sort('twmmid')
+			parent.show_mmid = True
+
+		def d_mmid(self,parent):
+			parent.show_mmid = not parent.show_mmid
+
+		def d_group(self,parent):
+			if parent.can_group:
+				parent.group = not parent.group
+
 	class item_action(TwCommon.item_action):
 
 		async def a_balance_refresh(self,uo,idx):