[view] Add View class for inheritance to aid in code reuse

master
Daniel Edgecumbe 7 years ago
parent b8f686aabc
commit 8c46e4b891

@ -8,7 +8,7 @@ import curses
import asyncio
# from decimal import Decimal
from macros import MIN_WINDOW_SIZE
import view
class BlockStore(object):
@ -119,22 +119,19 @@ class BlockStore(object):
return self._bestblockhash
class BlockView(object):
class BlockView(view.View):
_mode_name = "block"
def __init__(self, blockstore, txidsetter, modesetter):
self._blockstore = blockstore
self._txidsetter = txidsetter
self._modesetter = modesetter
self._pad = None
self._visible = False
self._hash = None # currently browsed hash.
self._selected_tx = None # (index, blockhash)
self._tx_offset = None # (offset, blockhash)
self._window_size = MIN_WINDOW_SIZE
super().__init__()
async def _set_hash(self, newhash):
# TODO: lock?
@ -209,28 +206,20 @@ class BlockView(object):
else:
self._pad.addstr(8+i-offset, 36, "{}".format(txid))
async def _draw(self):
self._clear_init_pad()
async def _draw(self, block, bestblockhash):
# TODO: figure out window width etc.
if self._pad is not None:
self._pad.clear()
else:
self._pad = curses.newpad(20, 100)
block = None
bestblockhash = None
if self._hash:
block = await self._blockstore.get_block(self._hash)
bestblockhash = await self._blockstore.get_bestblockhash()
if block:
await self._draw_block(block, bestblockhash)
await self._draw_transactions(block, bestblockhash)
await self._draw_pad_to_screen()
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
self._pad.refresh(0, 0, 4, 0, min(maxy-3, 24), min(maxx-1, 100))
self._draw_pad_to_screen()
async def _select_previous_transaction(self):
if self._hash is None:
@ -250,7 +239,7 @@ class BlockView(object):
self._selected_tx = (self._selected_tx[0] - 1, self._selected_tx[1])
await self.draw()
await self._draw_if_visible()
async def _select_next_transaction(self):
if self._hash is None:
@ -275,7 +264,7 @@ class BlockView(object):
self._selected_tx = (self._selected_tx[0] + 1, self._selected_tx[1])
await self.draw()
await self._draw_if_visible()
async def _enter_transaction_view(self):
if self._hash is None:
@ -307,7 +296,7 @@ class BlockView(object):
return # Can't do anything
await self._set_hash(newhash)
await self.draw()
await self._draw_if_visible()
async def _select_next_block(self):
if self._hash is None:
@ -319,7 +308,7 @@ class BlockView(object):
return # Can't do anything
await self._set_hash(newhash)
await self.draw()
await self._draw_if_visible()
async def _select_previous_block_n(self, n):
if self._hash is None:
@ -331,7 +320,7 @@ class BlockView(object):
return # Can't do anything
await self._set_hash(newhash)
await self.draw()
await self._draw_if_visible()
async def _select_next_block_n(self, n):
if self._hash is None:
@ -343,7 +332,7 @@ class BlockView(object):
return # Can't do anything
await self._set_hash(newhash)
await self.draw()
await self._draw_if_visible()
async def _select_best_block(self):
if self._hash is None:
@ -355,19 +344,7 @@ class BlockView(object):
return # Can't do anything
await self._set_hash(newhash)
await self.draw()
async def draw(self):
if not self._visible:
return
block = None
bestblockhash = None
if self._hash:
block = await self._blockstore.get_block(self._hash)
bestblockhash = await self._blockstore.get_bestblockhash()
await self._draw(block, bestblockhash)
await self._draw_if_visible()
async def on_bestblockhash(self, key, obj):
try:
@ -383,21 +360,7 @@ class BlockView(object):
await self._set_hash(newhash)
# Redraw so that we know if it's the best
await self.draw()
async def on_mode_change(self, newmode):
if newmode != "block":
self._visible = False
return
self._visible = True
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
if self._visible:
await self.draw()
await self._draw_if_visible()
async def handle_keypress(self, key):
assert self._visible

@ -8,9 +8,11 @@ VERSION_STRING = "bitcoind-ncurses v0.2.0-dev"
# "monitor", "wallet", "peers", "block",
# "tx", "console", "net", "forks",
# ]
MODES = ["monitor", "peers", "block", "transaction"]
MODES = ["monitor", "peers", "block", "transaction", "net"]
DEFAULT_MODE = "monitor"
# TX_VERBOSE_MODE controls whether the prevouts for an input are fetched.
# TX_VERBOSE_MODE = True
TX_VERBOSE_MODE = False
MIN_WINDOW_SIZE = (10, 20)

@ -8,17 +8,15 @@ import curses
import asyncio
from decimal import Decimal
from macros import MIN_WINDOW_SIZE
import view
class MonitorView(object):
class MonitorView(view.View):
_mode_name = "monitor"
def __init__(self, client):
self._client = client
self._pad = None
self._visible = False
self._lock = asyncio.Lock()
self._bestblockhash = None
self._bestblockheader = None # raw json blockheader
@ -29,15 +27,10 @@ class MonitorView(object):
self._dt = None
self._uptime = None # raw uptime from bitcoind (seconds)
self._window_size = MIN_WINDOW_SIZE
super().__init__()
async def _draw(self):
# TODO: figure out window width etc.
if self._pad is not None:
self._pad.clear()
else:
self._pad = curses.newpad(20, 100)
self._clear_init_pad()
if self._bestblockheader:
bbh = self._bestblockheader
@ -132,16 +125,10 @@ class MonitorView(object):
if self._uptime:
self._pad.addstr(13, 1, "uptime: {}".format(datetime.timedelta(seconds=self._uptime)))
await self._draw_pad_to_screen()
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
self._draw_pad_to_screen()
self._pad.refresh(0, 0, 4, 0, min(maxy-3, 24), min(maxx-1, 100))
async def draw(self):
async def _draw_if_visible(self):
""" Override the view.View method because we need to lock. """
with await self._lock:
if self._visible:
await self._draw()
@ -168,7 +155,7 @@ class MonitorView(object):
self._bestcoinbase = j["result"]
if draw:
await self.draw()
await self._draw_if_visible()
async def on_mempoolinfo(self, key, obj):
try:
@ -176,7 +163,7 @@ class MonitorView(object):
except KeyError:
return
await self.draw()
await self._draw_if_visible()
async def on_estimatesmartfee(self, key, obj):
try:
@ -190,13 +177,13 @@ class MonitorView(object):
except KeyError:
self._estimatesmartfee = None
await self.draw()
await self._draw_if_visible()
async def on_tick(self, dt):
with await self._lock:
self._dt = dt
await self.draw()
await self._draw_if_visible()
async def on_uptime(self, key, obj):
try:
@ -204,17 +191,4 @@ class MonitorView(object):
except KeyError:
return
await self.draw()
async def on_mode_change(self, newmode):
if newmode != "monitor":
self._visible = False
return
self._visible = True
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
await self.draw()
await self._draw_if_visible()

@ -6,27 +6,40 @@ import math
import curses
import asyncio
from macros import MIN_WINDOW_SIZE
import view
class NetView(object):
class NetView(view.View):
_mode_name = "net"
def __init__(self):
self._pad = None
self._nettotals_history = []
self._visible = False
super().__init__()
self._nettotals_history = []
async def _draw(self):
self._clear_init_pad()
self._window_size = MIN_WINDOW_SIZE
deltas = []
if self._nettotals_history:
hist = self._nettotals_history
i = 1
while i < len(hist):
prev = hist[i-1]
current = hist[i]
seconds = (current["timemillis"] - prev["timemillis"]) / 1000
if seconds <= 0:
continue
up = current["totalbytessent"] - prev["totalbytessent"]
down = current["totalbytesrecv"] - prev["totalbytesrecv"]
async def _draw(self, deltas):
ph, pw = 20, 100
deltas.append(
(up/seconds, down/seconds),
)
if self._pad is not None:
self._pad.clear()
else:
self._pad = curses.newpad(ph, pw)
i += 1
ph, pw = 20, 100
plot_height = (ph-3) // 2
plot_offset = plot_height
chart_offset = 13
@ -74,36 +87,6 @@ class NetView(object):
self._draw_pad_to_screen()
def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
self._pad.refresh(0, 0, 4, 0, min(maxy-3, maxy-2), min(maxx-1, 100))
async def draw(self):
if self._visible:
deltas = []
if self._nettotals_history:
hist = self._nettotals_history
i = 1
while i < len(hist):
prev = hist[i-1]
current = hist[i]
seconds = (current["timemillis"] - prev["timemillis"]) / 1000
if seconds <= 0:
continue
up = current["totalbytessent"] - prev["totalbytessent"]
down = current["totalbytesrecv"] - prev["totalbytesrecv"]
deltas.append(
(up/seconds, down/seconds),
)
i += 1
await self._draw(deltas)
async def on_nettotals(self, key, obj):
try:
self._nettotals_history.append(obj["result"])
@ -114,17 +97,4 @@ class NetView(object):
if len(self._nettotals_history) > 500:
self._nettotals_history = self._nettotals_history[:300]
await self.draw()
async def on_mode_change(self, newmode):
if newmode != "net":
self._visible = False
return
self._visible = True
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
await self.draw()
await self._draw_if_visible()

@ -8,23 +8,19 @@ import math
import curses
import asyncio
from macros import MIN_WINDOW_SIZE
import view
class PeersView(view.View):
_mode_name = "peers"
class PeersView(object):
def __init__(self):
self._pad = None
self._visible = False
self._peerinfo = None # raw data from getpeerinfo
self._window_size = MIN_WINDOW_SIZE
super().__init__()
async def _draw(self):
# TODO: figure out window width etc.
if self._pad is not None:
self._pad.clear()
else:
self._pad = curses.newpad(20, 100) # y, x
self._clear_init_pad()
if self._peerinfo:
po = self._peerinfo
@ -88,18 +84,7 @@ class PeersView(object):
if 'synced_headers' in peer:
self._pad.addstr(1+index-offset, 93, str(peer['synced_headers']).rjust(7) )
await self._draw_pad_to_screen()
async def draw(self):
if self._visible:
await self._draw()
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
self._pad.refresh(0, 0, 4, 0, min(maxy-3, 24), min(maxx-1, 100))
self._draw_pad_to_screen()
async def on_peerinfo(self, key, obj):
try:
@ -107,17 +92,4 @@ class PeersView(object):
except KeyError:
return
await self.draw()
async def on_mode_change(self, newmode):
if newmode != "peers":
self._visible = False
return
self._visible = True
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
await self.draw()
await self._draw_if_visible()

@ -24,6 +24,7 @@ splash_array = [
width = len(splash_array[0])
height = len(splash_array)
# TODO: potentially move SplashView over to view.View
class SplashView(object):
def __init__(self, set_mode_callback):
self._set_mode_callback = set_mode_callback # ModeHandler

@ -6,7 +6,8 @@ import datetime
import curses
import asyncio
from macros import MIN_WINDOW_SIZE, TX_VERBOSE_MODE
import view
from macros import TX_VERBOSE_MODE
class TransactionStore(object):
@ -28,21 +29,19 @@ class TransactionStore(object):
return j["result"]
class TransactionView(object):
class TransactionView(view.View):
_mode_name = "transaction"
def __init__(self, transactionstore):
self._transactionstore = transactionstore
self._pad = None
self._visible = False
self._txid = None # currently browsed txid.
self._selected_input = None # (index, txid)
self._input_offset = None # (offset, txid)
self._selected_output = None # (index, txid)
self._output_offset = None # (offset, txid)
self._window_size = MIN_WINDOW_SIZE
super().__init__()
async def _set_txid(self, txid, vout=None):
# TODO: lock?
@ -196,13 +195,22 @@ class TransactionView(object):
self._pad.addstr(14+i-offset, 1, "{:05d} {} {: 15.8f} BTC".format(i, outstring, out["value"]), outputcolor)
async def _draw(self, transaction, inouts):
# TODO: figure out window width etc.
async def _draw(self):
self._clear_init_pad()
if self._pad is not None:
self._pad.clear()
else:
self._pad = curses.newpad(20, 100)
transaction = None
inouts = None
if self._txid:
transaction = await self._transactionstore.get_transaction(self._txid)
if TX_VERBOSE_MODE:
inouts = []
for vin in transaction["vin"]:
if not "txid" in vin:
# It's a coinbase
inouts = None
break
prevtx = await self._transactionstore.get_transaction(vin["txid"])
inouts.append(prevtx["vout"][vin["vout"]])
if transaction:
await self._draw_transaction(transaction)
@ -211,14 +219,7 @@ class TransactionView(object):
if "vout" in transaction:
await self._draw_outputs(transaction)
await self._draw_pad_to_screen()
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
self._pad.refresh(0, 0, 4, 0, min(maxy-3, 24), min(maxx-1, 100))
self._draw_pad_to_screen()
async def _select_previous_input(self):
if self._txid is None:
@ -238,7 +239,7 @@ class TransactionView(object):
self._selected_input = (self._selected_input[0] - 1, self._selected_input[1])
await self.draw()
await self._draw_if_visible()
async def _select_next_input(self):
if self._txid is None:
@ -263,7 +264,7 @@ class TransactionView(object):
self._selected_input = (self._selected_input[0] + 1, self._selected_input[1])
await self.draw()
await self._draw_if_visible()
async def _select_input_as_transaction(self):
if self._txid is None:
@ -287,7 +288,7 @@ class TransactionView(object):
else:
await self._set_txid(inp["txid"], vout=inp["vout"])
await self.draw()
await self._draw_if_visible()
async def _select_previous_output(self):
if self._txid is None:
@ -307,7 +308,7 @@ class TransactionView(object):
self._selected_output = (self._selected_output[0] - 1, self._selected_output[1])
await self.draw()
await self._draw_if_visible()
async def _select_next_output(self):
if self._txid is None:
@ -332,41 +333,7 @@ class TransactionView(object):
self._selected_output = (self._selected_output[0] + 1, self._selected_output[1])
await self.draw()
async def draw(self):
if not self._visible:
return
transaction = None
inouts = None
if self._txid:
transaction = await self._transactionstore.get_transaction(self._txid)
if TX_VERBOSE_MODE:
inouts = []
for vin in transaction["vin"]:
if not "txid" in vin:
# It's a coinbase
inouts = None
break
prevtx = await self._transactionstore.get_transaction(vin["txid"])
inouts.append(prevtx["vout"][vin["vout"]])
await self._draw(transaction, inouts)
async def on_mode_change(self, newmode):
if newmode != "transaction":
self._visible = False
return
self._visible = True
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
if self._visible:
await self.draw()
await self._draw_if_visible()
async def handle_keypress(self, key):
assert self._visible
@ -391,6 +358,4 @@ class TransactionView(object):
await self._select_input_as_transaction()
return None
raise Exception(key)
return key

@ -0,0 +1,49 @@
# Copyright (c) 2014-2017 esotericnonsense (Daniel Edgecumbe)
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/licenses/mit-license.php
import curses
import asyncio
from macros import MIN_WINDOW_SIZE
class View(object):
""" Handles basic operations for the central views. """
def __init__(self):
self._pad = None
self._visible = False
self._nettotals_history = []
self._window_size = MIN_WINDOW_SIZE
def _clear_init_pad(self):
if self._pad is not None:
self._pad.clear()
else:
self._pad = curses.newpad(20, 100)
def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
self._pad.refresh(0, 0, 4, 0, min(maxy-3, maxy-2), min(maxx-1, 100))
async def _draw_if_visible(self):
if self._visible:
await self._draw()
async def on_mode_change(self, newmode):
if newmode != self._mode_name:
self._visible = False
return
self._visible = True
await self._draw_if_visible()
async def on_window_resize(self, y, x):
# All of the current views assume an x width of 100.
self._window_size = (y, x)
await self._draw_if_visible()
Loading…
Cancel
Save