commit 587f8aa48702496407f5044fa3a3d9f9aae36f34
parent 8010123c0858bf9735cdfc6637899799ea6891f2
Author: ThomasV <thomasv@electrum.org>
Date: Thu, 22 Aug 2019 18:48:52 +0200
Kivy GUI improvements:
- create unique instances of channels_dialog and addresses_dialog
- display and refresh balances in channels_dialog
- improve formatting of tx history
- repurpose left button in receive_tab
Diffstat:
6 files changed, 147 insertions(+), 64 deletions(-)
diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
@@ -333,6 +333,8 @@ class ElectrumWindow(App):
# cached dialogs
self._settings_dialog = None
self._password_dialog = None
+ self._channels_dialog = None
+ self._addresses_dialog = None
self.fee_status = self.electrum_config.get_fee_status()
self.request_popup = None
@@ -666,8 +668,9 @@ class ElectrumWindow(App):
d.open()
def lightning_channels_dialog(self):
- d = LightningChannelsDialog(self)
- d.open()
+ if self._channels_dialog is None:
+ self._channels_dialog = LightningChannelsDialog(self)
+ self._channels_dialog.open()
def popup_dialog(self, name):
if name == 'settings':
@@ -1054,20 +1057,12 @@ class ElectrumWindow(App):
popup.update()
popup.open()
- def requests_dialog(self, screen):
- from .uix.dialogs.requests import RequestsDialog
- if len(self.wallet.get_sorted_requests(self.electrum_config)) == 0:
- self.show_info(_('No saved requests.'))
- return
- popup = RequestsDialog(self, screen, None)
- popup.update()
- popup.open()
-
def addresses_dialog(self):
from .uix.dialogs.addresses import AddressesDialog
- popup = AddressesDialog(self)
- popup.update()
- popup.open()
+ if self._addresses_dialog is None:
+ self._addresses_dialog = AddressesDialog(self)
+ self._addresses_dialog.update()
+ self._addresses_dialog.open()
def fee_dialog(self, label, dt):
from .uix.dialogs.fee_dialog import FeeDialog
diff --git a/electrum/gui/kivy/uix/dialogs/lightning_channels.py b/electrum/gui/kivy/uix/dialogs/lightning_channels.py
@@ -6,26 +6,52 @@ from kivy.uix.popup import Popup
from kivy.clock import Clock
from electrum.gui.kivy.uix.context_menu import ContextMenu
from electrum.util import bh2u
-from electrum.lnutil import LOCAL, REMOTE
+from electrum.lnutil import LOCAL, REMOTE, format_short_channel_id
from electrum.gui.kivy.i18n import _
Builder.load_string(r'''
<LightningChannelItem@CardItem>
details: {}
active: False
- channelId: '<channelId not set>'
- id: card
+ short_channel_id: '<channelId not set>'
+ status: ''
+ local_balance: ''
+ remote_balance: ''
_chan: None
- Label:
- color: (.5,.5,.5,1) if not card.active else (1,1,1,1)
- text: root.channelId
- Label:
- text: (card._chan.get_state() if card._chan else 'n/a')
-
+ BoxLayout:
+ spacing: '8dp'
+ height: '32dp'
+ orientation: 'vertical'
+ Widget
+ CardLabel:
+ color: (.5,.5,.5,1) if not root.active else (1,1,1,1)
+ text: root.short_channel_id
+ font_size: '15sp'
+ Widget
+ CardLabel:
+ font_size: '13sp'
+ shorten: True
+ text: root.status
+ Widget
+ BoxLayout:
+ spacing: '8dp'
+ height: '32dp'
+ orientation: 'vertical'
+ Widget
+ CardLabel:
+ text: root.local_balance
+ font_size: '13sp'
+ halign: 'right'
+ Widget
+ CardLabel:
+ text: root.remote_balance
+ font_size: '13sp'
+ halign: 'right'
+ Widget
<LightningChannelsDialog@Popup>:
name: 'lightning_channels'
- title: _('Lightning channels. Tap for options.')
+ title: _('Lightning channels.')
id: popup
BoxLayout:
id: box
@@ -103,12 +129,13 @@ class LightningChannelsDialog(Factory.Popup):
self.clocks = []
self.app = app
self.context_menu = None
- self.app.wallet.network.register_callback(self.channels_update, ['channels'])
- self.channels_update('bogus evt')
+ self.app.wallet.network.register_callback(self.on_channels, ['channels'])
+ self.app.wallet.network.register_callback(self.on_channel, ['channel'])
+ self.update()
def show_channel_details(self, obj):
p = Factory.ChannelDetailsPopup()
- p.title = _('Details for channel ') + self.presentable_chan_id(obj._chan)
+ p.title = _('Details for channel ') + format_short_channel_id(obj.chan.short_channel_id)
p.data = [{'keyName': key, 'value': str(obj.details[key])} for key in obj.details.keys()]
p.open()
@@ -146,10 +173,37 @@ class LightningChannelsDialog(Factory.Popup):
self.ids.box.remove_widget(self.context_menu)
self.context_menu = None
- def presentable_chan_id(self, i):
- return bh2u(i.short_channel_id) if i.short_channel_id else bh2u(i.channel_id)[:16]
-
- def channels_update(self, evt):
+ def format_fields(self, chan):
+ labels = {}
+ for subject in (REMOTE, LOCAL):
+ bal_minus_htlcs = chan.balance_minus_outgoing_htlcs(subject)//1000
+ label = self.app.format_amount(bal_minus_htlcs)
+ other = subject.inverted()
+ bal_other = chan.balance(other)//1000
+ bal_minus_htlcs_other = chan.balance_minus_outgoing_htlcs(other)//1000
+ if bal_other != bal_minus_htlcs_other:
+ label += ' (+' + self.app.format_amount(bal_other - bal_minus_htlcs_other) + ')'
+ labels[subject] = label
+ return [
+ labels[LOCAL],
+ labels[REMOTE],
+ ]
+
+ def on_channel(self, evt, chan):
+ Clock.schedule_once(lambda dt: self.update())
+
+ def on_channels(self, evt):
+ Clock.schedule_once(lambda dt: self.update())
+
+ def update_item(self, item):
+ chan = item._chan
+ item.status = chan.get_state()
+ item.short_channel_id = format_short_channel_id(chan.short_channel_id)
+ l, r = self.format_fields(chan)
+ item.local_balance = _('Local') + ':' + l
+ item.remote_balance = _('Remote') + ': ' + r
+
+ def update(self):
channel_cards = self.ids.lightning_channels_container
channel_cards.clear_widgets()
if not self.app.wallet:
@@ -158,10 +212,10 @@ class LightningChannelsDialog(Factory.Popup):
for i in lnworker.channels.values():
item = Factory.LightningChannelItem()
item.screen = self
- item.channelId = self.presentable_chan_id(i)
item.active = i.node_id in lnworker.peers
item.details = self.channel_details(i)
item._chan = i
+ self.update_item(item)
channel_cards.add_widget(item)
def channel_details(self, chan):
diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
@@ -34,6 +34,7 @@ from electrum.lnaddr import lndecode
from electrum.lnutil import RECEIVED, SENT, PaymentFailure
from .context_menu import ContextMenu
+from .dialogs.question import Question
from .dialogs.lightning_open_channel import LightningOpenChannelDialog
from electrum.gui.kivy.i18n import _
@@ -132,15 +133,15 @@ class HistoryScreen(CScreen):
self.menu_actions = [ ('Label', self.label_dialog), ('Details', self.show_tx)]
def show_tx(self, obj):
- tx_hash = obj.tx_hash
- tx = self.app.wallet.db.get_transaction(tx_hash)
+ key = obj.key
+ tx = self.app.wallet.db.get_transaction(key)
if not tx:
return
self.app.tx_dialog(tx)
def label_dialog(self, obj):
from .dialogs.label_dialog import LabelDialog
- key = obj.tx_hash
+ key = obj.key
text = self.app.wallet.get_label(key)
def callback(text):
self.app.wallet.set_label(key, text)
@@ -151,14 +152,13 @@ class HistoryScreen(CScreen):
def get_card(self, tx_item): #tx_hash, tx_mined_status, value, balance):
is_lightning = tx_item.get('lightning', False)
timestamp = tx_item['timestamp']
+ key = tx_item.get('txid') or tx_item['payment_hash']
if is_lightning:
status = 0
txpos = tx_item['txpos']
- if timestamp is None:
- status_str = 'unconfirmed'
- else:
- status_str = format_time(int(timestamp))
+ status_str = 'unconfirmed' if timestamp is None else format_time(int(timestamp))
icon = "atlas://electrum/gui/kivy/theming/light/lightning"
+ message = tx_item['label']
else:
tx_hash = tx_item['txid']
conf = tx_item['confirmations']
@@ -169,18 +169,19 @@ class HistoryScreen(CScreen):
timestamp=tx_item['timestamp'])
status, status_str = self.app.wallet.get_tx_status(tx_hash, tx_mined_info)
icon = "atlas://electrum/gui/kivy/theming/light/" + TX_ICONS[status]
+ message = tx_item['label'] or tx_hash
ri = {}
ri['screen'] = self
+ ri['key'] = key
ri['icon'] = icon
ri['date'] = status_str
- ri['message'] = tx_item['label']
+ ri['message'] = message
value = tx_item['value'].value
if value is not None:
ri['is_mine'] = value < 0
- if value < 0: value = - value
- ri['amount'] = self.app.format_amount_and_units(value)
+ ri['amount'] = self.app.format_amount(value, is_diff = True)
if 'fiat_value' in tx_item:
- ri['quote_text'] = tx_item['fiat_value'].to_ui_string()
+ ri['quote_text'] = str(tx_item['fiat_value'])
return ri
def update(self, see_all=False):
@@ -344,7 +345,6 @@ class SendScreen(CScreen):
message = self.screen.message
amount = sum(map(lambda x:x[2], outputs))
if self.app.electrum_config.get('use_rbf'):
- from .dialogs.question import Question
d = Question(_('Should this transaction be replaceable?'), lambda b: self._do_send(amount, message, outputs, b))
d.open()
else:
@@ -406,7 +406,7 @@ class ReceiveScreen(CScreen):
def __init__(self, **kwargs):
super(ReceiveScreen, self).__init__(**kwargs)
- self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.do_delete)]
+ self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.delete_request_dialog)]
Clock.schedule_interval(lambda dt: self.update(), 5)
def expiry(self):
@@ -509,17 +509,27 @@ class ReceiveScreen(CScreen):
d = ChoiceDialog(_('Expiration date'), pr_expiration_values, self.expiry(), callback)
d.open()
- def do_delete(self, req):
- from .dialogs.question import Question
+ def clear_requests_dialog(self):
+ expired = [req for req in self.app.wallet.get_sorted_requests(self.app.electrum_config) if req['status'] == PR_EXPIRED]
+ if len(expired) == 0:
+ return
+ def callback(c):
+ if c:
+ for req in expired:
+ is_lightning = req.get('lightning', False)
+ key = req['rhash'] if is_lightning else req['address']
+ self.app.wallet.delete_request(key)
+ self.update()
+ d = Question(_('Delete expired requests?'), callback)
+ d.open()
+
+ def delete_request_dialog(self, req):
def cb(result):
if result:
- if req.is_lightning:
- self.app.wallet.lnworker.delete_invoice(req.key)
- else:
- self.app.wallet.remove_payment_request(req.key, self.app.electrum_config)
+ self.app.wallet.delete_request(req.key)
self.hide_menu()
self.update()
- d = Question(_('Delete request'), cb)
+ d = Question(_('Delete request?'), cb)
d.open()
def show_menu(self, obj):
diff --git a/electrum/gui/kivy/uix/ui_screens/history.kv b/electrum/gui/kivy/uix/ui_screens/history.kv
@@ -7,11 +7,9 @@
<CardLabel@Label>
- color: 0.95, 0.95, 0.95, 1
- size_hint: 1, None
- text: ''
+ color: .7, .7, .7, 1
text_size: self.width, None
- height: self.texture_size[1]
+ #height: self.texture_size[1]
halign: 'left'
valign: 'top'
@@ -21,11 +19,12 @@
message: ''
is_mine: True
amount: '--'
- action: _('Sent') if self.is_mine else _('Received')
amount_color: '#FF6657' if self.is_mine else '#2EA442'
confirmations: 0
date: ''
quote_text: ''
+ amount_str: self.quote_text if app.is_fiat else self.amount
+ unit_str: app.fx.ccy if app.is_fiat else app.base_unit
Image:
id: icon
source: root.icon
@@ -34,18 +33,36 @@
width: self.height*1.5
mipmap: True
BoxLayout:
+ spacing: '8dp'
+ height: '32dp'
orientation: 'vertical'
Widget
CardLabel:
- text:
- u'[color={color}]{s}[/color]'.format(s='<<' if root.is_mine else '>>', color=root.amount_color)\
- + ' ' + root.action + ' ' + (root.quote_text if app.is_fiat else root.amount)
+ color: 0.95, 0.95, 0.95, 1
+ text: root.message
+ shorten: True
+ shorten_from: 'right'
font_size: '15sp'
+ Widget
CardLabel:
- color: .699, .699, .699, 1
- font_size: '14sp'
+ font_size: '12sp'
shorten: True
- text: root.date + ' ' + root.message
+ text: root.date
+ Widget
+ BoxLayout:
+ spacing: '8dp'
+ height: '32dp'
+ orientation: 'vertical'
+ Widget
+ CardLabel:
+ text: u'[color={color}]{s}[/color]'.format(s=root.amount_str, color=root.amount_color) + ' ' + '[size=12sp]' + root.unit_str + '[/size]'
+ halign: 'right'
+ font_size: '15sp'
+ Widget
+ CardLabel:
+ text: ''
+ halign: 'right'
+ font_size: '12sp'
Widget
<HistoryRecycleView>:
diff --git a/electrum/gui/kivy/uix/ui_screens/receive.kv b/electrum/gui/kivy/uix/ui_screens/receive.kv
@@ -135,7 +135,7 @@ ReceiveScreen:
icon: 'atlas://electrum/gui/kivy/theming/light/list'
size_hint: 0.5, None
height: '48dp'
- on_release: Clock.schedule_once(lambda dt: app.addresses_dialog())
+ on_release: Clock.schedule_once(lambda dt: s.clear_requests_dialog())
IconButton:
icon: 'atlas://electrum/gui/kivy/theming/light/clock1'
size_hint: 0.5, None
diff --git a/electrum/wallet.py b/electrum/wallet.py
@@ -1356,6 +1356,13 @@ class Abstract_Wallet(AddressSynchronizer):
f.write(json.dumps(req))
return req
+ def delete_request(self, key):
+ """ lightning or on-chain """
+ if key in self.receive_requests:
+ self.remove_payment_request(key, {})
+ elif self.lnworker:
+ self.lnworker.delete_invoice(key)
+
def remove_payment_request(self, addr, config):
if addr not in self.receive_requests:
return False