Browse Source

Merge pull request #9256 from spesmilo/rm_blocking_dialog

qt: replace BlockingWaitingDialog with RunCoroutineDialog
master
ThomasV 1 year ago committed by GitHub
parent
commit
0ec60095f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      electrum/gui/qt/confirm_tx_dialog.py
  2. 32
      electrum/gui/qt/main_window.py
  3. 40
      electrum/gui/qt/swap_dialog.py
  4. 8
      electrum/gui/qt/transaction_dialog.py
  5. 54
      electrum/gui/qt/util.py

6
electrum/gui/qt/confirm_tx_dialog.py

@ -42,7 +42,7 @@ from electrum.simple_config import SimpleConfig
from electrum.bitcoin import DummyAddress
from .util import (WindowModalDialog, ColorScheme, HelpLabel, Buttons, CancelButton,
BlockingWaitingDialog, PasswordLineEdit, WWLabel, read_QIcon)
PasswordLineEdit, WWLabel, read_QIcon)
from .fee_slider import FeeSlider, FeeComboBox
@ -532,7 +532,7 @@ class TxEditor(WindowModalDialog):
if self.not_enough_funds:
self.io_widget.update(None)
self.set_feerounding_visibility(False)
self.messages = []
self.messages = [_('Preparing transaction...')]
else:
self.messages = self.get_messages()
self.update_fee_fields()
@ -619,7 +619,7 @@ class ConfirmTxDialog(TxEditor):
title=_("New Transaction"), # todo: adapt title for channel funding tx, swaps
allow_preview=allow_preview)
BlockingWaitingDialog(window, _("Preparing transaction..."), self.update)
self.trigger_update()
def _update_amount_label(self):
tx = self.tx

32
electrum/gui/qt/main_window.py

@ -90,7 +90,7 @@ from .util import (read_QIcon, ColorScheme, text_dialog, icon_path, WaitingDialo
import_meta_gui, export_meta_gui,
filename_field, address_field, char_width_in_lineedit, webopen,
TRANSACTION_FILE_EXTENSION_FILTER_ANY, MONOSPACE_FONT,
getOpenFileName, getSaveFileName, BlockingWaitingDialog, font_height)
getOpenFileName, getSaveFileName, font_height)
from .util import ButtonsLineEdit, ShowQRLineEdit
from .util import QtEventListener, qt_event_listener, event_listener
from .wizard.wallet import WIF_HELP_TEXT
@ -301,26 +301,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self._update_check_thread.checked.connect(on_version_received)
self._update_check_thread.start()
def run_coroutine_dialog(self, coro, text, on_result, on_cancelled):
""" run coroutine in a waiting dialog, with a Cancel button that cancels the coroutine """
from electrum import util
loop = util.get_asyncio_loop()
assert util.get_running_loop() != loop, 'must not be called from asyncio thread'
future = asyncio.run_coroutine_threadsafe(coro, loop)
def task():
try:
return future.result()
except concurrent.futures.CancelledError:
on_cancelled()
try:
WaitingDialog(
self, text, task,
on_success=on_result,
on_error=self.on_error,
on_cancel=future.cancel)
except Exception as e:
self.show_error(str(e))
raise
def run_coroutine_dialog(self, coro, text):
""" run coroutine in a waiting dialog, with a Cancel button that cancels the coroutine"""
from .util import RunCoroutineDialog
d = RunCoroutineDialog(self, text, coro)
return d.run()
def run_coroutine_from_thread(self, coro, name, on_result=None):
if self._cleaned_up:
@ -1180,10 +1165,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
if not self.wallet.lnworker.num_sats_can_send() and not self.wallet.lnworker.num_sats_can_receive():
self.show_error(_("You do not have liquidity in your active channels."))
return
def get_pairs_thread():
self.network.run_from_another_thread(self.wallet.lnworker.swap_manager.get_pairs())
try:
BlockingWaitingDialog(self, _('Please wait...'), get_pairs_thread)
self.run_coroutine_dialog(
self.wallet.lnworker.swap_manager.get_pairs(), _('Please wait...'))
except SwapServerError as e:
self.show_error(str(e))
return

40
electrum/gui/qt/swap_dialog.py

@ -4,7 +4,7 @@ from PyQt6.QtCore import pyqtSignal
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QGridLayout, QPushButton
from electrum.i18n import _
from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates
from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates, UserCancelled
from electrum.bitcoin import DummyAddress
from electrum.transaction import PartialTxOutput, PartialTransaction
@ -319,25 +319,33 @@ class SwapDialog(WindowModalDialog, QtEventListener):
recv_amount = self.recv_amount_e.get_amount()
self.ok_button.setEnabled(bool(send_amount) and bool(recv_amount))
def do_normal_swap(self, lightning_amount, onchain_amount, password):
async def _do_normal_swap(self, lightning_amount, onchain_amount, password):
dummy_tx = self._create_tx(onchain_amount)
assert dummy_tx
sm = self.swap_manager
swap, invoice = await sm.request_normal_swap(
lightning_amount_sat=lightning_amount,
expected_onchain_amount_sat=onchain_amount,
channels=self.channels,
)
self._current_swap = swap
tx = sm.create_funding_tx(swap, dummy_tx, password=password)
txid = await sm.wait_for_htlcs_and_broadcast(swap=swap, invoice=invoice, tx=tx)
return txid
def do_normal_swap(self, lightning_amount, onchain_amount, password):
self._current_swap = None
async def coro():
swap, invoice = await sm.request_normal_swap(
lightning_amount_sat=lightning_amount,
expected_onchain_amount_sat=onchain_amount,
channels=self.channels,
)
self._current_swap = swap
tx = sm.create_funding_tx(swap, dummy_tx, password=password)
txid = await sm.wait_for_htlcs_and_broadcast(swap=swap, invoice=invoice, tx=tx)
return txid
self.window.run_coroutine_dialog(
coro(), _('Awaiting swap payment...'),
on_result=lambda funding_txid: self.window.on_swap_result(funding_txid, is_reverse=False),
on_cancelled=lambda: sm.cancel_normal_swap(self._current_swap))
coro = self._do_normal_swap(lightning_amount, onchain_amount, password)
try:
funding_txid = self.window.run_coroutine_dialog(coro, _('Awaiting swap payment...'))
except UserCancelled:
self.swap_manager.cancel_normal_swap(self._current_swap)
self.window.show_message(_('Swap cancelled'))
return
except Exception as e:
self.window.show_error(str(e))
return
self.window.on_swap_result(funding_txid, is_reverse=False)
def get_description(self):
onchain_funds = "onchain funds"

8
electrum/gui/qt/transaction_dialog.py

@ -62,7 +62,7 @@ from .util import (MessageBoxMixin, read_QIcon, Buttons, icon_path,
char_width_in_lineedit, TRANSACTION_FILE_EXTENSION_FILTER_SEPARATE,
TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX,
TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX,
BlockingWaitingDialog, getSaveFileName, ColorSchemeItem,
getSaveFileName, ColorSchemeItem,
get_iconname_qrcode, VLine, WaitingDialog)
from .rate_limiter import rate_limited
from .my_treeview import create_toolbar_with_menu, QMenuWithConfig
@ -582,11 +582,9 @@ class TxDialog(QDialog, MessageBoxMixin):
# FIXME for PSBTs, we do a blocking fetch, as the missing data might be needed for e.g. signing
# - otherwise, the missing data is for display-completeness only, e.g. fee, input addresses (we do it async)
if not tx.is_complete() and tx.is_missing_info_from_network():
BlockingWaitingDialog(
self,
self.main_window.run_coroutine_dialog(
tx.add_info_from_network(self.wallet.network, timeout=10),
_("Adding info to tx, from network..."),
lambda: Network.run_from_another_thread(
tx.add_info_from_network(self.wallet.network, timeout=10)),
)
else:
self.maybe_fetch_txin_data()

54
electrum/gui/qt/util.py

@ -21,7 +21,7 @@ from PyQt6.QtWidgets import (QPushButton, QLabel, QMessageBox, QHBoxLayout, QVBo
from electrum.i18n import _
from electrum.util import FileImportFailed, FileExportFailed, resource_path
from electrum.util import EventListener, event_listener, get_logger
from electrum.util import EventListener, event_listener, get_logger, UserCancelled
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, PR_BROADCASTING, PR_BROADCAST
from electrum.logging import Logger
from electrum.qrreader import MissingQrDetectionLib
@ -371,32 +371,32 @@ class WaitingDialog(WindowModalDialog):
self.message_label.setText(msg)
class BlockingWaitingDialog(WindowModalDialog):
"""Shows a waiting dialog whilst running a task.
Should be called from the GUI thread. The GUI thread will be blocked while
the task is running; the point of the dialog is to provide feedback
to the user regarding what is going on.
"""
def __init__(self, parent: QWidget, message: str, task: Callable[[], Any]):
assert parent
if isinstance(parent, MessageBoxMixin):
parent = parent.top_level_window()
WindowModalDialog.__init__(self, parent, _("Please wait"))
self.message_label = QLabel(message)
vbox = QVBoxLayout(self)
vbox.addWidget(self.message_label)
self.finished.connect(self.deleteLater) # see #3956
# show popup
self.show()
# refresh GUI; needed for popup to appear and for message_label to get drawn
QCoreApplication.processEvents()
QCoreApplication.processEvents()
try:
# block and run given task
task()
finally:
# close popup
self.accept()
class RunCoroutineDialog(WaitingDialog):
def __init__(self, parent: QWidget, message: str, coroutine):
from electrum import util
import asyncio
import concurrent.futures
loop = util.get_asyncio_loop()
assert util.get_running_loop() != loop, 'must not be called from asyncio thread'
self._exception = None
self._result = None
self._future = asyncio.run_coroutine_threadsafe(coroutine, loop)
def task():
try:
self._result = self._future.result()
except concurrent.futures.CancelledError:
self._exception = UserCancelled
except Exception as e:
self._exception = e
WaitingDialog.__init__(self, parent, message, task, on_cancel=self._future.cancel)
def run(self):
self.exec()
if self._exception:
raise self._exception
else:
return self._result
def line_dialog(parent, title, label, ok_label, default=None):

Loading…
Cancel
Save