1
0

qt: refactor send_tab, paytoedit

This commit is contained in:
Sander van Grieken
2023-06-20 20:54:31 +02:00
parent d9a43fa6ed
commit bde066f9ce
5 changed files with 340 additions and 243 deletions

View File

@@ -13,7 +13,6 @@ from electrum.util import (format_satoshis_plain, decimal_point_to_base_unit_nam
FEERATE_PRECISION, quantize_feerate, DECIMAL_POINT)
from electrum.bitcoin import COIN, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
_NOT_GIVEN = object() # sentinel value

View File

@@ -26,6 +26,8 @@
from functools import partial
from typing import NamedTuple, Sequence, Optional, List, TYPE_CHECKING
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QFontMetrics, QFont
from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QTextEdit, QVBoxLayout
@@ -48,6 +50,10 @@ frozen_style = "QWidget {border:none;}"
normal_style = "QPlainTextEdit { }"
class InvalidPaymentIdentifier(Exception):
pass
class ResizingTextEdit(QTextEdit):
def __init__(self):
@@ -63,113 +69,139 @@ class ResizingTextEdit(QTextEdit):
self.verticalMargins += documentMargin * 2
self.heightMin = self.fontSpacing + self.verticalMargins
self.heightMax = (self.fontSpacing * 10) + self.verticalMargins
self.single_line = True
self.update_size()
def update_size(self):
docLineCount = self.document().lineCount()
docHeight = max(3, docLineCount) * self.fontSpacing
docHeight = max(1 if self.single_line else 3, docLineCount) * self.fontSpacing
h = docHeight + self.verticalMargins
h = min(max(h, self.heightMin), self.heightMax)
self.setMinimumHeight(int(h))
self.setMaximumHeight(int(h))
self.verticalScrollBar().setHidden(docHeight + self.verticalMargins < self.heightMax)
if self.single_line:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
else:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.verticalScrollBar().setHidden(docHeight + self.verticalMargins < self.heightMax)
self.setLineWrapMode(QTextEdit.LineWrapMode.WidgetWidth)
class PayToEdit(QObject, Logger, GenericInputHandler):
class PayToEdit(Logger, GenericInputHandler):
paymentIdentifierChanged = pyqtSignal()
def __init__(self, send_tab: 'SendTab'):
QObject.__init__(self, parent=send_tab)
Logger.__init__(self)
GenericInputHandler.__init__(self)
self.line_edit = QLineEdit()
self.text_edit = ResizingTextEdit()
self.text_edit.hide()
self.text_edit.textChanged.connect(self._on_text_edit_text_changed)
self._is_paytomany = False
for w in [self.line_edit, self.text_edit]:
w.setFont(QFont(MONOSPACE_FONT))
w.textChanged.connect(self._on_text_changed)
self.text_edit.setFont(QFont(MONOSPACE_FONT))
self.send_tab = send_tab
self.config = send_tab.config
self.win = send_tab.window
self.app = QApplication.instance()
self.amount_edit = self.send_tab.amount_e
self.logger.debug(util.ColorScheme.RED.as_stylesheet(True))
self.is_multiline = False
self.disable_checks = False
self.is_alias = False
# self.is_alias = False
self.payto_scriptpubkey = None # type: Optional[bytes]
self.previous_payto = ''
# editor methods
self.setStyleSheet = self.editor.setStyleSheet
self.setText = self.editor.setText
self.setEnabled = self.editor.setEnabled
self.setReadOnly = self.editor.setReadOnly
self.setFocus = self.editor.setFocus
self.setStyleSheet = self.text_edit.setStyleSheet
self.setText = self.text_edit.setText
self.setFocus = self.text_edit.setFocus
self.setToolTip = self.text_edit.setToolTip
# button handlers
self.on_qr_from_camera_input_btn = partial(
self.input_qr_from_camera,
config=self.config,
allow_multi=False,
show_error=self.win.show_error,
setText=self._on_input_btn,
parent=self.win,
show_error=self.send_tab.show_error,
setText=self.try_payment_identifier,
parent=self.send_tab.window,
)
self.on_qr_from_screenshot_input_btn = partial(
self.input_qr_from_screenshot,
allow_multi=False,
show_error=self.win.show_error,
setText=self._on_input_btn,
show_error=self.send_tab.show_error,
setText=self.try_payment_identifier,
)
self.on_input_file = partial(
self.input_file,
config=self.config,
show_error=self.win.show_error,
setText=self._on_input_btn,
show_error=self.send_tab.show_error,
setText=self.try_payment_identifier,
)
#
self.line_edit.contextMenuEvent = partial(editor_contextMenuEvent, self.line_edit, self)
self.text_edit.contextMenuEvent = partial(editor_contextMenuEvent, self.text_edit, self)
@property
def editor(self):
return self.text_edit if self.is_paytomany() else self.line_edit
self.payment_identifier = None
def set_text(self, text: str):
self.text_edit.setText(text)
def update_editor(self):
if self.text_edit.toPlainText() != self.payment_identifier.text:
self.text_edit.setText(self.payment_identifier.text)
self.text_edit.single_line = not self.payment_identifier.is_multiline()
self.text_edit.update_size()
'''set payment identifier only if valid, else exception'''
def try_payment_identifier(self, text):
text = text.strip()
pi = PaymentIdentifier(self.send_tab.wallet, text)
if not pi.is_valid():
raise InvalidPaymentIdentifier('Invalid payment identifier')
self.set_payment_identifier(text)
def set_payment_identifier(self, text):
text = text.strip()
if self.payment_identifier and self.payment_identifier.text == text:
# no change.
return
self.payment_identifier = PaymentIdentifier(self.send_tab.wallet, text)
# toggle to multiline if payment identifier is a multiline
self.is_multiline = self.payment_identifier.is_multiline()
self.logger.debug(f'is_multiline {self.is_multiline}')
if self.is_multiline and not self._is_paytomany:
self.set_paytomany(True)
# if payment identifier gets set externally, we want to update the text_edit
# Note: this triggers the change handler, but we shortcut if it's the same payment identifier
self.update_editor()
self.paymentIdentifierChanged.emit()
def set_paytomany(self, b):
has_focus = self.editor.hasFocus()
self._is_paytomany = b
self.line_edit.setVisible(not b)
self.text_edit.setVisible(b)
self.text_edit.single_line = not self._is_paytomany
self.text_edit.update_size()
self.send_tab.paytomany_menu.setChecked(b)
if has_focus:
self.editor.setFocus()
def toggle_paytomany(self):
self.set_paytomany(not self._is_paytomany)
def toPlainText(self):
return self.text_edit.toPlainText() if self.is_paytomany() else self.line_edit.text()
def is_paytomany(self):
return self._is_paytomany
def setFrozen(self, b):
self.setReadOnly(b)
self.text_edit.setReadOnly(b)
if not b:
self.setStyleSheet(normal_style)
def setTextNoCheck(self, text: str):
"""Sets the text, while also ensuring the new value will not be resolved/checked."""
self.previous_payto = text
self.setText(text)
def isFrozen(self):
return self.text_edit.isReadOnly()
def do_clear(self):
self.is_multiline = False
self.set_paytomany(False)
self.disable_checks = False
self.is_alias = False
self.line_edit.setText('')
self.text_edit.setText('')
self.setFrozen(False)
self.setEnabled(True)
self.payment_identifier = None
def setGreen(self):
self.setStyleSheet(util.ColorScheme.GREEN.as_stylesheet(True))
@@ -177,53 +209,18 @@ class PayToEdit(Logger, GenericInputHandler):
def setExpired(self):
self.setStyleSheet(util.ColorScheme.RED.as_stylesheet(True))
def _on_input_btn(self, text: str):
self.setText(text)
def _on_text_edit_text_changed(self):
self._handle_text_change(self.text_edit.toPlainText())
def _on_text_changed(self):
text = self.toPlainText()
# False if user pasted from clipboard
full_check = self.app.clipboard().text() != text
self._check_text(text, full_check=full_check)
if self.is_multiline and not self._is_paytomany:
self.set_paytomany(True)
self.text_edit.setText(text)
self.text_edit.setFocus()
def _handle_text_change(self, text):
if self.isFrozen():
# if editor is frozen, we ignore text changes as they might not be a payment identifier
# but a user friendly representation.
return
def _check_text(self, text, *, full_check: bool):
""" side effects: self.is_multiline """
text = str(text).strip()
if not text:
return
if self.previous_payto == text:
return
if full_check:
self.previous_payto = text
if self.disable_checks:
return
pi = PaymentIdentifier(self.send_tab.wallet, text)
self.is_multiline = bool(pi.multiline_outputs) # TODO: why both is_multiline and set_paytomany(True)??
self.logger.debug(f'is_multiline {self.is_multiline}')
if pi.is_valid():
self.send_tab.set_payment_identifier(text)
else:
if not full_check and pi.error:
self.send_tab.show_error(
_('Clipboard text is not a valid payment identifier') + '\n' + str(pi.error))
return
def handle_multiline(self, outputs):
total = 0
is_max = False
for output in outputs:
if parse_max_spend(output.value):
is_max = True
else:
total += output.value
self.send_tab.set_onchain(True)
self.send_tab.max_button.setChecked(is_max)
if self.send_tab.max_button.isChecked():
self.send_tab.spend_max()
else:
self.amount_edit.setAmount(total if outputs else None)
#self.send_tab.lock_amount(self.send_tab.max_button.isChecked() or bool(outputs))
self.set_payment_identifier(text)
if self.app.clipboard().text() and self.app.clipboard().text().strip() == self.payment_identifier.text:
# user pasted from clipboard
self.logger.debug('from clipboard')
if self.payment_identifier.error:
self.send_tab.show_error(_('Clipboard text is not a valid payment identifier') + '\n' + self.payment_identifier.error)

View File

@@ -6,14 +6,13 @@ import asyncio
from decimal import Decimal
from typing import Optional, TYPE_CHECKING, Sequence, List, Callable
from PyQt5.QtCore import pyqtSignal, QPoint
from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QGridLayout,
QHBoxLayout, QCompleter, QWidget, QToolTip, QPushButton)
from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QGridLayout, QHBoxLayout,
QWidget, QToolTip, QPushButton, QApplication)
from electrum.plugin import run_hook
from electrum.i18n import _
from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates, parse_max_spend
from electrum.payment_identifier import PaymentIdentifier
from electrum.invoices import PR_PAID, Invoice, PR_BROADCASTING, PR_BROADCAST
from electrum.transaction import Transaction, PartialTxInput, PartialTxOutput
@@ -21,8 +20,10 @@ from electrum.network import TxBroadcastError, BestEffortRequestFailed
from electrum.logging import Logger
from .amountedit import AmountEdit, BTCAmountEdit, SizedFreezableLineEdit
from .util import WaitingDialog, HelpLabel, MessageBoxMixin, EnterButton, char_width_in_lineedit
from .util import get_iconname_camera, get_iconname_qrcode, read_QIcon
from .paytoedit import InvalidPaymentIdentifier
from .util import (WaitingDialog, HelpLabel, MessageBoxMixin, EnterButton,
char_width_in_lineedit, get_iconname_camera, get_iconname_qrcode,
read_QIcon)
from .confirm_tx_dialog import ConfirmTxDialog
if TYPE_CHECKING:
@@ -38,7 +39,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
def __init__(self, window: 'ElectrumWindow'):
QWidget.__init__(self, window)
Logger.__init__(self)
self.app = QApplication.instance()
self.window = window
self.wallet = window.wallet
self.fx = window.fx
@@ -49,7 +50,6 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
self.format_amount = window.format_amount
self.base_unit = window.base_unit
self.payment_identifier = None
self.pending_invoice = None
# A 4-column grid layout. All the stretch is in the last column.
@@ -73,7 +73,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
"e.g. set one amount to '2!' and another to '3!' to split your coins 40-60."))
payto_label = HelpLabel(_('Pay to'), msg)
grid.addWidget(payto_label, 0, 0)
grid.addWidget(self.payto_e.line_edit, 0, 1, 1, 4)
# grid.addWidget(self.payto_e.line_edit, 0, 1, 1, 4)
grid.addWidget(self.payto_e.text_edit, 0, 1, 1, 4)
#completer = QCompleter()
@@ -119,11 +119,9 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
btn_width = 10 * char_width_in_lineedit()
self.max_button.setFixedWidth(btn_width)
self.max_button.setCheckable(True)
self.max_button.setEnabled(False)
grid.addWidget(self.max_button, 3, 3)
self.save_button = EnterButton(_("Save"), self.do_save_invoice)
self.send_button = EnterButton(_("Pay") + "...", self.do_pay_or_get_invoice)
self.clear_button = EnterButton(_("Clear"), self.do_clear)
self.paste_button = QPushButton()
self.paste_button.clicked.connect(self.do_paste)
self.paste_button.setIcon(read_QIcon('copy.png'))
@@ -131,9 +129,15 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
self.paste_button.setMaximumWidth(35)
grid.addWidget(self.paste_button, 0, 5)
self.save_button = EnterButton(_("Save"), self.do_save_invoice)
self.save_button.setEnabled(False)
self.send_button = EnterButton(_("Pay") + "...", self.do_pay_or_get_invoice)
self.send_button.setEnabled(False)
self.clear_button = EnterButton(_("Clear"), self.do_clear)
buttons = QHBoxLayout()
buttons.addStretch(1)
#buttons.addWidget(self.paste_button)
buttons.addWidget(self.clear_button)
buttons.addWidget(self.save_button)
buttons.addWidget(self.send_button)
@@ -143,14 +147,11 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
def reset_max(text):
self.max_button.setChecked(False)
enable = not bool(text) and not self.amount_e.isReadOnly()
# self.max_button.setEnabled(enable)
self.amount_e.textChanged.connect(self.on_amount_changed)
self.amount_e.textEdited.connect(reset_max)
self.fiat_send_e.textEdited.connect(reset_max)
self.set_onchain(False)
self.invoices_label = QLabel(_('Invoices'))
from .invoice_list import InvoiceList
self.invoice_list = InvoiceList(self)
@@ -184,30 +185,33 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
self.resolve_done_signal.connect(self.on_resolve_done)
self.finalize_done_signal.connect(self.on_finalize_done)
self.notify_merchant_done_signal.connect(self.on_notify_merchant_done)
self.payto_e.paymentIdentifierChanged.connect(self._handle_payment_identifier)
def on_amount_changed(self, text):
# FIXME: implement full valid amount check to enable/disable Pay button
pi_valid = self.payto_e.payment_identifier.is_valid() if self.payto_e.payment_identifier else False
self.send_button.setEnabled(bool(self.amount_e.get_amount()) and pi_valid)
def do_paste(self):
text = self.window.app.clipboard().text()
if not text:
return
self.set_payment_identifier(text)
try:
self.payto_e.try_payment_identifier(self.app.clipboard().text())
except InvalidPaymentIdentifier as e:
self.show_error(_('Invalid payment identifier on clipboard'))
def set_payment_identifier(self, text):
self.payment_identifier = PaymentIdentifier(self.wallet, text)
if self.payment_identifier.error:
self.show_error(_('Clipboard text is not a valid payment identifier') + '\n' + self.payment_identifier.error)
return
if self.payment_identifier.is_multiline():
self.payto_e.set_paytomany(True)
self.payto_e.text_edit.setText(text)
else:
self.payto_e.setTextNoCheck(text)
self._handle_payment_identifier(can_use_network=True)
self.logger.debug('set_payment_identifier')
try:
self.payto_e.try_payment_identifier(text)
except InvalidPaymentIdentifier as e:
self.show_error(_('Invalid payment identifier'))
def spend_max(self):
assert self.payto_e.payment_identifier is not None
assert self.payto_e.payment_identifier.type in ['spk', 'multiline']
if run_hook('abort_send', self):
return
amount = self.get_amount()
outputs = self.payment_identifier.get_onchain_outputs(amount)
outputs = self.payto_e.payment_identifier.get_onchain_outputs('!')
if not outputs:
return
make_tx = lambda fee_est, *, confirmed_only=False: self.wallet.make_unsigned_transaction(
@@ -296,9 +300,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
text = _("Not enough funds")
frozen_str = self.get_frozen_balance_str()
if frozen_str:
text += " ({} {})".format(
frozen_str, _("are frozen")
)
text += " ({} {})".format(frozen_str, _("are frozen"))
return text
def get_frozen_balance_str(self) -> Optional[str]:
@@ -308,31 +310,26 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
return self.format_amount_and_units(frozen_bal)
def do_clear(self):
self.lock_fields(lock_recipient=False, lock_amount=False, lock_max=True, lock_description=False)
self.max_button.setChecked(False)
self.payto_e.do_clear()
self.set_onchain(False)
for w in [self.comment_e, self.comment_label]:
w.setVisible(False)
for e in [self.message_e, self.amount_e, self.fiat_send_e]:
e.setText('')
self.set_field_style(e, None, False)
for e in [self.send_button, self.save_button, self.clear_button, self.amount_e, self.fiat_send_e]:
e.setEnabled(True)
for e in [self.save_button, self.send_button]:
e.setEnabled(False)
self.window.update_status()
run_hook('do_clear', self)
def set_onchain(self, b):
self._is_onchain = b
self.max_button.setEnabled(b)
def prepare_for_send_tab_network_lookup(self):
self.window.show_send_tab()
self.payto_e.disable_checks = True
#for e in [self.payto_e, self.message_e]:
self.payto_e.setFrozen(True)
# self.payto_e.setFrozen(True)
for btn in [self.save_button, self.send_button, self.clear_button]:
btn.setEnabled(False)
self.payto_e.setTextNoCheck(_("please wait..."))
# self.payto_e.setTextNoCheck(_("please wait..."))
def payment_request_error(self, error):
self.show_message(error)
@@ -348,45 +345,90 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
style = ColorScheme.RED.as_stylesheet(True)
if text is not None:
w.setStyleSheet(style)
w.setReadOnly(True)
else:
w.setStyleSheet('')
w.setReadOnly(False)
def lock_fields(self, *,
lock_recipient: Optional[bool] = None,
lock_amount: Optional[bool] = None,
lock_max: Optional[bool] = None,
lock_description: Optional[bool] = None
) -> None:
self.logger.debug(f'locking fields, r={lock_recipient}, a={lock_amount}, m={lock_max}, d={lock_description}')
if lock_recipient is not None:
self.payto_e.setFrozen(lock_recipient)
if lock_amount is not None:
self.amount_e.setFrozen(lock_amount)
if lock_max is not None:
self.max_button.setEnabled(not lock_max)
if lock_description is not None:
self.message_e.setFrozen(lock_description)
def update_fields(self):
recipient, amount, description, comment, validated = self.payment_identifier.get_fields_for_GUI()
if recipient:
self.payto_e.setTextNoCheck(recipient)
elif self.payment_identifier.multiline_outputs:
self.payto_e.handle_multiline(self.payment_identifier.multiline_outputs)
if description:
self.message_e.setText(description)
if amount:
self.amount_e.setAmount(amount)
for w in [self.comment_e, self.comment_label]:
w.setVisible(not bool(comment))
self.set_field_style(self.payto_e, recipient or self.payment_identifier.multiline_outputs, validated)
self.set_field_style(self.message_e, description, validated)
self.set_field_style(self.amount_e, amount, validated)
self.set_field_style(self.fiat_send_e, amount, validated)
pi = self.payto_e.payment_identifier
def _handle_payment_identifier(self, *, can_use_network: bool = True):
is_valid = self.payment_identifier.is_valid()
self.save_button.setEnabled(is_valid)
self.send_button.setEnabled(is_valid)
if not is_valid:
if pi.is_multiline():
self.lock_fields(lock_recipient=False, lock_amount=True, lock_max=True, lock_description=False)
self.set_field_style(self.payto_e, pi.multiline_outputs, False if not pi.is_valid() else None)
self.save_button.setEnabled(pi.is_valid())
self.send_button.setEnabled(pi.is_valid())
if pi.is_valid():
self.handle_multiline(pi.multiline_outputs)
else:
# self.payto_e.setToolTip('\n'.join(list(map(lambda x: f'{x.idx}: {x.line_content}', pi.get_error()))))
self.payto_e.setToolTip(pi.get_error())
return
if not pi.is_valid():
self.lock_fields(lock_recipient=False, lock_amount=False, lock_max=True, lock_description=False)
self.save_button.setEnabled(False)
self.send_button.setEnabled(False)
return
lock_recipient = pi.type != 'spk'
self.lock_fields(lock_recipient=lock_recipient,
lock_amount=pi.is_amount_locked(),
lock_max=pi.is_amount_locked(),
lock_description=False)
if lock_recipient:
recipient, amount, description, comment, validated = pi.get_fields_for_GUI()
if recipient:
self.payto_e.setText(recipient)
if description:
self.message_e.setText(description)
self.lock_fields(lock_description=True)
if amount:
self.amount_e.setAmount(amount)
for w in [self.comment_e, self.comment_label]:
w.setVisible(bool(comment))
self.set_field_style(self.payto_e, recipient or pi.multiline_outputs, validated)
self.set_field_style(self.message_e, description, validated)
self.set_field_style(self.amount_e, amount, validated)
self.set_field_style(self.fiat_send_e, amount, validated)
self.send_button.setEnabled(bool(self.amount_e.get_amount()) and not pi.has_expired())
self.save_button.setEnabled(True)
def _handle_payment_identifier(self):
is_valid = self.payto_e.payment_identifier.is_valid()
self.logger.debug(f'handle PI, valid={is_valid}')
self.update_fields()
if self.payment_identifier.need_resolve():
if not is_valid:
self.logger.debug(f'PI error: {self.payto_e.payment_identifier.error}')
return
if self.payto_e.payment_identifier.need_resolve():
self.prepare_for_send_tab_network_lookup()
self.payment_identifier.resolve(on_finished=self.resolve_done_signal.emit)
# update fiat amount
self.payto_e.payment_identifier.resolve(on_finished=self.resolve_done_signal.emit)
# update fiat amount (and reset max)
self.amount_e.textEdited.emit("")
self.window.show_send_tab()
def on_resolve_done(self, pi):
if self.payment_identifier.error:
self.show_error(self.payment_identifier.error)
if self.payto_e.payment_identifier.error:
self.show_error(self.payto_e.payment_identifier.error)
self.do_clear()
return
self.update_fields()
@@ -404,10 +446,10 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
self.show_error(_('No amount'))
return
invoice = self.payment_identifier.get_invoice(amount_sat, self.get_message())
invoice = self.payto_e.payment_identifier.get_invoice(amount_sat, self.get_message())
#except Exception as e:
if not invoice:
self.show_error('error getting invoice' + self.payment_identifier.error)
self.show_error('error getting invoice' + self.payto_e.payment_identifier.error)
return
if not self.wallet.has_lightning() and not invoice.can_be_paid_onchain():
self.show_error(_('Lightning is disabled'))
@@ -439,18 +481,17 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
return self.amount_e.get_amount() or 0
def on_finalize_done(self, pi):
self.do_clear()
if pi.error:
self.show_error(pi.error)
self.do_clear()
return
self.update_fields(pi)
self.update_fields()
invoice = pi.get_invoice(self.get_amount(), self.get_message())
self.pending_invoice = invoice
self.logger.debug(f'after finalize invoice: {invoice!r}')
self.do_pay_invoice(invoice)
def do_pay_or_get_invoice(self):
pi = self.payment_identifier
pi = self.payto_e.payment_identifier
if pi.need_finalize():
self.prepare_for_send_tab_network_lookup()
pi.finalize(amount_sat=self.get_amount(), comment=self.message_e.text(),
@@ -511,9 +552,9 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
"""Returns whether there are errors.
Also shows error dialog to user if so.
"""
error = self.payment_identifier.get_error()
error = self.payto_e.payment_identifier.get_error()
if error:
if not self.payment_identifier.is_multiline():
if not self.payto_e.payment_identifier.is_multiline():
err = error
self.show_warning(
_("Failed to parse 'Pay to' line") + ":\n" +
@@ -527,13 +568,13 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
# for err in errors]))
return True
warning = self.payment_identifier.warning
warning = self.payto_e.payment_identifier.warning
if warning:
warning += '\n' + _('Do you wish to continue?')
if not self.question(warning):
return True
if self.payment_identifier.has_expired():
if self.payto_e.payment_identifier.has_expired():
self.show_error(_('Payment request has expired'))
return True
@@ -619,7 +660,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
def broadcast_thread():
# non-GUI thread
if self.payment_identifier.has_expired():
if self.payto_e.payment_identifier.has_expired():
return False, _("Invoice has expired")
try:
self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
@@ -629,9 +670,9 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
return False, repr(e)
# success
txid = tx.txid()
if self.payment_identifier.need_merchant_notify():
if self.payto_e.payment_identifier.need_merchant_notify():
refund_address = self.wallet.get_receiving_address()
self.payment_identifier.notify_merchant(
self.payto_e.payment_identifier.notify_merchant(
tx=tx,
refund_address=refund_address,
on_finished=self.notify_merchant_done_signal.emit
@@ -683,10 +724,23 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
self.window.show_send_tab()
self.payto_e.do_clear()
if len(paytos) == 1:
self.logger.debug('payto_e setText 1')
self.payto_e.setText(paytos[0])
self.amount_e.setFocus()
else:
self.payto_e.setFocus()
text = "\n".join([payto + ", 0" for payto in paytos])
self.logger.debug('payto_e setText n')
self.payto_e.setText(text)
self.payto_e.setFocus()
def handle_multiline(self, outputs):
total = 0
for output in outputs:
if parse_max_spend(output.value):
self.max_button.setChecked(True) # TODO: remove and let spend_max set this?
self.spend_max()
return
else:
total += output.value
self.amount_e.setAmount(total if outputs else None)

View File

@@ -562,7 +562,10 @@ class GenericInputHandler:
new_text = self.text() + data + '\n'
else:
new_text = data
setText(new_text)
try:
setText(new_text)
except Exception as e:
show_error(_('Invalid payment identifier in QR') + ':\n' + repr(e))
from .qrreader import scan_qrcode
if parent is None:
@@ -599,7 +602,10 @@ class GenericInputHandler:
new_text = self.text() + data + '\n'
else:
new_text = data
setText(new_text)
try:
setText(new_text)
except Exception as e:
show_error(_('Invalid payment identifier in QR') + ':\n' + repr(e))
def input_file(
self,
@@ -628,7 +634,10 @@ class GenericInputHandler:
except BaseException as e:
show_error(_('Error opening file') + ':\n' + repr(e))
else:
setText(data)
try:
setText(data)
except Exception as e:
show_error(_('Invalid payment identifier in file') + ':\n' + repr(e))
def input_paste_from_clipboard(
self,