electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 7bb4ea150f8cbc9da3edc589c94883c195a72d9a
parent 2af178a5864891fa003aa037b35a6c813c658096
Author: ThomasV <thomasv@electrum.org>
Date:   Wed, 30 Jan 2019 11:10:11 +0100

gui: show incoming lightning requests, add on-chain icon

Diffstat:
Melectrum/gui/qt/invoice_list.py | 51+++++++++++++++++++++++++++++++++++++--------------
Melectrum/gui/qt/main_window.py | 6++++--
Melectrum/gui/qt/request_list.py | 58+++++++++++++++++++++++++---------------------------------
Melectrum/gui/qt/util.py | 3++-
Melectrum/paymentrequest.py | 3+++
Aicons/bitcoin.png | 0
6 files changed, 71 insertions(+), 50 deletions(-)

diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py @@ -30,7 +30,9 @@ from PyQt5.QtGui import QStandardItemModel, QStandardItem, QFont from PyQt5.QtWidgets import QHeaderView, QMenu from electrum.i18n import _ -from electrum.util import format_time +from electrum.util import format_time, pr_tooltips, PR_UNPAID +from electrum.lnutil import lndecode +from electrum.bitcoin import COIN from .util import (MyTreeView, read_QIcon, MONOSPACE_FONT, PR_UNPAID, pr_tooltips, import_meta_gui, export_meta_gui, pr_icons) @@ -40,26 +42,23 @@ class InvoiceList(MyTreeView): class Columns(IntEnum): DATE = 0 - REQUESTOR = 1 - DESCRIPTION = 2 - AMOUNT = 3 - STATUS = 4 + DESCRIPTION = 1 + AMOUNT = 2 + STATUS = 3 headers = { Columns.DATE: _('Expires'), - Columns.REQUESTOR: _('Requestor'), Columns.DESCRIPTION: _('Description'), Columns.AMOUNT: _('Amount'), Columns.STATUS: _('Status'), } - filter_columns = [Columns.DATE, Columns.REQUESTOR, Columns.DESCRIPTION, Columns.AMOUNT] + filter_columns = [Columns.DATE, Columns.DESCRIPTION, Columns.AMOUNT] def __init__(self, parent): super().__init__(parent, self.create_menu, stretch_column=self.Columns.DESCRIPTION, editable_columns=[]) self.setSortingEnabled(True) - self.setColumnWidth(self.Columns.REQUESTOR, 200) self.setModel(QStandardItemModel(self)) self.update() @@ -67,26 +66,50 @@ class InvoiceList(MyTreeView): inv_list = self.parent.invoices.unpaid_invoices() self.model().clear() self.update_headers(self.__class__.headers) - self.header().setSectionResizeMode(self.Columns.REQUESTOR, QHeaderView.Interactive) for idx, pr in enumerate(inv_list): key = pr.get_id() status = self.parent.invoices.get_status(key) if status is None: continue requestor = pr.get_requestor() - exp = pr.get_expiration_date() + exp = pr.get_time() date_str = format_time(exp) if exp else _('Never') - labels = [date_str, requestor, pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')] + labels = [date_str, '[%s] '%requestor + pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')] items = [QStandardItem(e) for e in labels] self.set_editability(items) + items[self.Columns.DATE].setIcon(read_QIcon('bitcoin.png')) items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status))) items[self.Columns.DATE].setData(key, role=Qt.UserRole) - items[self.Columns.REQUESTOR].setFont(QFont(MONOSPACE_FONT)) - items[self.Columns.AMOUNT].setFont(QFont(MONOSPACE_FONT)) self.model().insertRow(idx, items) + + lnworker = self.parent.wallet.lnworker + for key, (preimage_hex, invoice, is_received, pay_timestamp) in lnworker.invoices.items(): + if is_received: + continue + status = lnworker.get_invoice_status(key) + lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP) + amount_sat = lnaddr.amount*COIN if lnaddr.amount else None + amount_str = self.parent.format_amount(amount_sat) if amount_sat else '' + description = '' + for k,v in lnaddr.tags: + if k == 'd': + description = v + break + date_str = format_time(lnaddr.date) + labels = [date_str, description, amount_str, pr_tooltips.get(status,'')] + items = [QStandardItem(e) for e in labels] + #items[0].setData(REQUEST_TYPE_LN, ROLE_REQUEST_TYPE) + #items[0].setData(key, ROLE_RHASH_OR_ADDR) + items[0].setIcon(self.icon_cache.get(':icons/lightning.png')) + items[3].setIcon(self.icon_cache.get(pr_icons.get(status))) + self.model().insertRow(self.model().rowCount(), items) + self.selectionModel().select(self.model().index(0,0), QItemSelectionModel.SelectCurrent) + # sort requests by date + self.model().sort(0) + # hide list if empty if self.parent.isVisible(): - b = len(inv_list) > 0 + b = self.model().rowCount() > 0 self.setVisible(b) self.parent.invoices_label.setVisible(b) self.filter() diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py @@ -953,8 +953,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): grid.addWidget(self.expires_label, 2, 1) self.create_invoice_button = QPushButton(_('On-chain')) + self.create_invoice_button.setIcon(QIcon(":icons/bitcoin.png")) self.create_invoice_button.clicked.connect(lambda: self.create_invoice(False)) self.create_lightning_invoice_button = QPushButton(_('Lightning')) + self.create_lightning_invoice_button.setIcon(QIcon(":icons/lightning.png")) self.create_lightning_invoice_button.clicked.connect(lambda: self.create_invoice(True)) self.receive_buttons = buttons = QHBoxLayout() buttons.addStretch(1) @@ -974,7 +976,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.receive_qr.enterEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.PointingHandCursor)) self.receive_qr.leaveEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.ArrowCursor)) - self.receive_requests_label = QLabel(_('Requests')) + self.receive_requests_label = QLabel(_('Incoming invoices')) from .request_list import RequestList self.request_list = RequestList(self) @@ -1395,7 +1397,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.fee_e.textChanged.connect(entry_changed) self.feerate_e.textChanged.connect(entry_changed) - self.invoices_label = QLabel(_('Invoices')) + self.invoices_label = QLabel(_('Outgoing invoices')) from .invoice_list import InvoiceList self.invoice_list = InvoiceList(self) diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py @@ -51,14 +51,12 @@ class RequestList(MyTreeView): class Columns(IntEnum): DATE = 0 - TYPE = 1 - DESCRIPTION = 2 - AMOUNT = 3 - STATUS = 4 + DESCRIPTION = 1 + AMOUNT = 2 + STATUS = 3 headers = { Columns.DATE: _('Date'), - Columns.TYPE: _('Type'), Columns.DESCRIPTION: _('Description'), Columns.AMOUNT: _('Amount'), Columns.STATUS: _('Status'), @@ -68,7 +66,7 @@ class RequestList(MyTreeView): def __init__(self, parent): super().__init__(parent, self.create_menu, stretch_column=self.Columns.DESCRIPTION, - editable_columns=[]) + editable_columns=[self.Columns.AMOUNT]) self.setModel(QStandardItemModel(self)) self.setSortingEnabled(True) self.update() @@ -76,7 +74,7 @@ class RequestList(MyTreeView): def select_key(self, key): for i in range(self.model().rowCount()): - item = self.model().index(i, 0) + item = self.model().index(i, self.Columns.DATE) row_key = item.data(ROLE_RHASH_OR_ADDR) if item.data(ROLE_REQUEST_TYPE) == REQUEST_TYPE_LN: row_key = self.wallet.lnworker.invoices[row_key][1] @@ -86,7 +84,7 @@ class RequestList(MyTreeView): def item_changed(self, idx): # TODO use siblingAtColumn when min Qt version is >=5.11 - item = self.model().itemFromIndex(idx.sibling(idx.row(), 0)) + item = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE)) request_type = item.data(ROLE_REQUEST_TYPE) key = item.data(ROLE_RHASH_OR_ADDR) if request_type == REQUEST_TYPE_BITCOIN: @@ -104,19 +102,8 @@ class RequestList(MyTreeView): def update(self): self.wallet = self.parent.wallet - # hide receive tab if no receive requests available - if self.parent.isVisible(): - b = len(self.wallet.receive_requests) > 0 or len(self.wallet.lnworker.invoices) > 0 - self.setVisible(b) - self.parent.receive_requests_label.setVisible(b) - if not b: - self.parent.expires_label.hide() - self.parent.expires_combo.show() - domain = self.wallet.get_receiving_addresses() - self.parent.update_receive_address_styling() - self.model().clear() self.update_headers(self.__class__.headers) for req in self.wallet.get_sorted_requests(self.config): @@ -132,17 +119,18 @@ class RequestList(MyTreeView): signature = req.get('sig') requestor = req.get('name', '') amount_str = self.parent.format_amount(amount) if amount else "" - labels = [date, 'on-chain', message, amount_str, pr_tooltips.get(status,'')] + labels = [date, message, amount_str, pr_tooltips.get(status,'')] items = [QStandardItem(e) for e in labels] self.set_editability(items) if signature is not None: - items[self.Columns.TYPE].setIcon(read_QIcon("seal.png")) - items[self.Columns.TYPE].setToolTip(f'signed by {requestor}') - if status is not PR_UNKNOWN: - items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status))) + items[self.Columns.DATE].setIcon(read_QIcon("seal.png")) + items[self.Columns.DATE].setToolTip(f'signed by {requestor}') + else: + items[self.Columns.DATE].setIcon(read_QIcon("bitcoin.png")) + items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status))) self.model().insertRow(self.model().rowCount(), items) - items[0].setData(REQUEST_TYPE_BITCOIN, ROLE_REQUEST_TYPE) - items[0].setData(address, ROLE_RHASH_OR_ADDR) + items[self.Columns.DATE].setData(REQUEST_TYPE_BITCOIN, ROLE_REQUEST_TYPE) + items[self.Columns.DATE].setData(address, ROLE_RHASH_OR_ADDR) self.filter() # lightning lnworker = self.wallet.lnworker @@ -159,16 +147,20 @@ class RequestList(MyTreeView): description = v break date = format_time(lnaddr.date) - labels = [date, 'lightning', description, amount_str, pr_tooltips.get(status,'')] + labels = [date, description, amount_str, pr_tooltips.get(status,'')] items = [QStandardItem(e) for e in labels] - items[1].setIcon(self.icon_cache.get(":icons/lightning.png")) - items[0].setData(REQUEST_TYPE_LN, ROLE_REQUEST_TYPE) - items[0].setData(key, ROLE_RHASH_OR_ADDR) - if status is not PR_UNKNOWN: - items[4].setIcon(self.icon_cache.get(pr_icons.get(status))) + items[self.Columns.DATE].setIcon(self.icon_cache.get(":icons/lightning.png")) + items[self.Columns.DATE].setData(REQUEST_TYPE_LN, ROLE_REQUEST_TYPE) + items[self.Columns.DATE].setData(key, ROLE_RHASH_OR_ADDR) + items[self.Columns.STATUS].setIcon(self.icon_cache.get(pr_icons.get(status))) self.model().insertRow(self.model().rowCount(), items) # sort requests by date - self.model().sort(0) + self.model().sort(self.Columns.DATE) + # hide list if empty + if self.parent.isVisible(): + b = self.model().rowCount() > 0 + self.setVisible(b) + self.parent.receive_requests_label.setVisible(b) def create_menu(self, position): idx = self.indexAt(position) diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py @@ -24,7 +24,7 @@ from PyQt5.QtWidgets import (QPushButton, QLabel, QMessageBox, QHBoxLayout, from electrum.i18n import _, languages from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, PrintError, resource_path -from electrum.util import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT +from electrum.util import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN if TYPE_CHECKING: from .main_window import ElectrumWindow @@ -41,6 +41,7 @@ else: dialogs = [] pr_icons = { + PR_UNKNOWN:"unpaid.png", PR_UNPAID:"unpaid.png", PR_PAID:"confirmed.png", PR_EXPIRED:"expired.png", diff --git a/electrum/paymentrequest.py b/electrum/paymentrequest.py @@ -246,6 +246,9 @@ class PaymentRequest: return None return self.details.expires and self.details.expires < int(time.time()) + def get_time(self): + return self.details.time + def get_expiration_date(self): return self.details.expires diff --git a/icons/bitcoin.png b/icons/bitcoin.png Binary files differ.