Browse Source

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
dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
ThomasV 5 years ago
parent
commit
587f8aa487
  1. 23
      electrum/gui/kivy/main_window.py
  2. 90
      electrum/gui/kivy/uix/dialogs/lightning_channels.py
  3. 50
      electrum/gui/kivy/uix/screens.py
  4. 39
      electrum/gui/kivy/uix/ui_screens/history.kv
  5. 2
      electrum/gui/kivy/uix/ui_screens/receive.kv
  6. 7
      electrum/wallet.py

23
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

90
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):

50
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):

39
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>:

2
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

7
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

Loading…
Cancel
Save