Browse Source

qt: MyTreeView: disambiguate "parent" and "main_window"

master
SomberNight 3 years ago
parent
commit
9d64fe7046
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 63
      electrum/gui/qt/address_list.py
  2. 71
      electrum/gui/qt/channels_list.py
  3. 32
      electrum/gui/qt/contact_list.py
  4. 70
      electrum/gui/qt/history_list.py
  5. 14
      electrum/gui/qt/invoice_list.py
  6. 16
      electrum/gui/qt/request_list.py
  7. 22
      electrum/gui/qt/util.py
  8. 53
      electrum/gui/qt/utxo_list.py
  9. 11
      electrum/gui/qt/watchtower_dialog.py

63
electrum/gui/qt/address_list.py

@ -25,6 +25,7 @@
import enum import enum
from enum import IntEnum from enum import IntEnum
from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, QPersistentModelIndex, QModelIndex from PyQt5.QtCore import Qt, QPersistentModelIndex, QModelIndex
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QFont from PyQt5.QtGui import QStandardItemModel, QStandardItem, QFont
@ -38,6 +39,9 @@ from electrum.wallet import InternalAddressCorruption
from .util import MyTreeView, MONOSPACE_FONT, ColorScheme, webopen, MySortModel from .util import MyTreeView, MONOSPACE_FONT, ColorScheme, webopen, MySortModel
if TYPE_CHECKING:
from .main_window import ElectrumWindow
class AddressUsageStateFilter(IntEnum): class AddressUsageStateFilter(IntEnum):
ALL = 0 ALL = 0
@ -85,12 +89,13 @@ class AddressList(MyTreeView):
ROLE_ADDRESS_STR = Qt.UserRole + 1001 ROLE_ADDRESS_STR = Qt.UserRole + 1001
key_role = ROLE_ADDRESS_STR key_role = ROLE_ADDRESS_STR
def __init__(self, parent): def __init__(self, main_window: 'ElectrumWindow'):
super().__init__(parent, self.create_menu, super().__init__(
stretch_column=self.Columns.LABEL, main_window=main_window,
editable_columns=[self.Columns.LABEL]) stretch_column=self.Columns.LABEL,
self.main_window = parent editable_columns=[self.Columns.LABEL],
self.wallet = self.parent.wallet )
self.wallet = self.main_window.wallet
self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setSortingEnabled(True) self.setSortingEnabled(True)
self.show_change = AddressTypeFilter.ALL # type: AddressTypeFilter self.show_change = AddressTypeFilter.ALL # type: AddressTypeFilter
@ -120,7 +125,7 @@ class AddressList(MyTreeView):
return toolbar return toolbar
def should_show_fiat(self): def should_show_fiat(self):
return self.parent.fx and self.parent.fx.is_enabled() and self.config.get('fiat_address', False) return self.main_window.fx and self.main_window.fx.is_enabled() and self.config.get('fiat_address', False)
def get_toolbar_buttons(self): def get_toolbar_buttons(self):
return self.change_button, self.used_button return self.change_button, self.used_button
@ -132,7 +137,7 @@ class AddressList(MyTreeView):
def refresh_headers(self): def refresh_headers(self):
if self.should_show_fiat(): if self.should_show_fiat():
ccy = self.parent.fx.get_currency() ccy = self.main_window.fx.get_currency()
else: else:
ccy = _('Fiat') ccy = _('Fiat')
headers = { headers = {
@ -171,7 +176,7 @@ class AddressList(MyTreeView):
self.proxy.setDynamicSortFilter(False) # temp. disable re-sorting after every change self.proxy.setDynamicSortFilter(False) # temp. disable re-sorting after every change
self.std_model.clear() self.std_model.clear()
self.refresh_headers() self.refresh_headers()
fx = self.parent.fx fx = self.main_window.fx
set_address = None set_address = None
num_shown = 0 num_shown = 0
self.addresses_beyond_gap_limit = self.wallet.get_all_known_addresses_beyond_gap_limit() self.addresses_beyond_gap_limit = self.wallet.get_all_known_addresses_beyond_gap_limit()
@ -236,9 +241,9 @@ class AddressList(MyTreeView):
num = self.wallet.adb.get_address_history_len(address) num = self.wallet.adb.get_address_history_len(address)
c, u, x = self.wallet.get_addr_balance(address) c, u, x = self.wallet.get_addr_balance(address)
balance = c + u + x balance = c + u + x
balance_text = self.parent.format_amount(balance, whitespaces=True) balance_text = self.main_window.format_amount(balance, whitespaces=True)
# create item # create item
fx = self.parent.fx fx = self.main_window.fx
if self.should_show_fiat(): if self.should_show_fiat():
rate = fx.exchange_rate() rate = fx.exchange_rate()
fiat_balance_str = fx.value_str(balance, rate) fiat_balance_str = fx.value_str(balance, rate)
@ -276,37 +281,37 @@ class AddressList(MyTreeView):
addr_column_title = self.std_model.horizontalHeaderItem(self.Columns.LABEL).text() addr_column_title = self.std_model.horizontalHeaderItem(self.Columns.LABEL).text()
addr_idx = idx.sibling(idx.row(), self.Columns.LABEL) addr_idx = idx.sibling(idx.row(), self.Columns.LABEL)
self.add_copy_menu(menu, idx) self.add_copy_menu(menu, idx)
menu.addAction(_('Details'), lambda: self.parent.show_address(addr)) menu.addAction(_('Details'), lambda: self.main_window.show_address(addr))
persistent = QPersistentModelIndex(addr_idx) persistent = QPersistentModelIndex(addr_idx)
menu.addAction(_("Edit {}").format(addr_column_title), lambda p=persistent: self.edit(QModelIndex(p))) menu.addAction(_("Edit {}").format(addr_column_title), lambda p=persistent: self.edit(QModelIndex(p)))
#menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr)) #menu.addAction(_("Request payment"), lambda: self.main_window.receive_at(addr))
if self.wallet.can_export(): if self.wallet.can_export():
menu.addAction(_("Private key"), lambda: self.parent.show_private_key(addr)) menu.addAction(_("Private key"), lambda: self.main_window.show_private_key(addr))
if not is_multisig and not self.wallet.is_watching_only(): if not is_multisig and not self.wallet.is_watching_only():
menu.addAction(_("Sign/verify message"), lambda: self.parent.sign_verify_message(addr)) menu.addAction(_("Sign/verify message"), lambda: self.main_window.sign_verify_message(addr))
menu.addAction(_("Encrypt/decrypt message"), lambda: self.parent.encrypt_message(addr)) menu.addAction(_("Encrypt/decrypt message"), lambda: self.main_window.encrypt_message(addr))
if can_delete: if can_delete:
menu.addAction(_("Remove from wallet"), lambda: self.parent.remove_address(addr)) menu.addAction(_("Remove from wallet"), lambda: self.main_window.remove_address(addr))
addr_URL = block_explorer_URL(self.config, 'addr', addr) addr_URL = block_explorer_URL(self.config, 'addr', addr)
if addr_URL: if addr_URL:
menu.addAction(_("View on block explorer"), lambda: webopen(addr_URL)) menu.addAction(_("View on block explorer"), lambda: webopen(addr_URL))
if not self.wallet.is_frozen_address(addr): if not self.wallet.is_frozen_address(addr):
menu.addAction(_("Freeze"), lambda: self.parent.set_frozen_state_of_addresses([addr], True)) menu.addAction(_("Freeze"), lambda: self.main_window.set_frozen_state_of_addresses([addr], True))
else: else:
menu.addAction(_("Unfreeze"), lambda: self.parent.set_frozen_state_of_addresses([addr], False)) menu.addAction(_("Unfreeze"), lambda: self.main_window.set_frozen_state_of_addresses([addr], False))
else: else:
# multiple items selected # multiple items selected
menu.addAction(_("Freeze"), lambda: self.parent.set_frozen_state_of_addresses(addrs, True)) menu.addAction(_("Freeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, True))
menu.addAction(_("Unfreeze"), lambda: self.parent.set_frozen_state_of_addresses(addrs, False)) menu.addAction(_("Unfreeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, False))
coins = self.wallet.get_spendable_coins(addrs) coins = self.wallet.get_spendable_coins(addrs)
if coins: if coins:
if self.parent.utxo_list.are_in_coincontrol(coins): if self.main_window.utxo_list.are_in_coincontrol(coins):
menu.addAction(_("Remove from coin control"), lambda: self.parent.utxo_list.remove_from_coincontrol(coins)) menu.addAction(_("Remove from coin control"), lambda: self.main_window.utxo_list.remove_from_coincontrol(coins))
else: else:
menu.addAction(_("Add to coin control"), lambda: self.parent.utxo_list.add_to_coincontrol(coins)) menu.addAction(_("Add to coin control"), lambda: self.main_window.utxo_list.add_to_coincontrol(coins))
run_hook('receive_menu', menu, addrs, self.wallet) run_hook('receive_menu', menu, addrs, self.wallet)
menu.exec_(self.viewport().mapToGlobal(position)) menu.exec_(self.viewport().mapToGlobal(position))
@ -316,7 +321,7 @@ class AddressList(MyTreeView):
try: try:
self.wallet.check_address_for_corruption(text) self.wallet.check_address_for_corruption(text)
except InternalAddressCorruption as e: except InternalAddressCorruption as e:
self.parent.show_error(str(e)) self.main_window.show_error(str(e))
raise raise
super().place_text_on_clipboard(text, title=title) super().place_text_on_clipboard(text, title=title)
@ -326,7 +331,7 @@ class AddressList(MyTreeView):
return self.get_role_data_from_coordinate(row, 0, role=self.ROLE_ADDRESS_STR) return self.get_role_data_from_coordinate(row, 0, role=self.ROLE_ADDRESS_STR)
def on_edited(self, idx, edit_key, *, text): def on_edited(self, idx, edit_key, *, text):
self.parent.wallet.set_label(edit_key, text) self.wallet.set_label(edit_key, text)
self.parent.history_model.refresh('address label edited') self.main_window.history_model.refresh('address label edited')
self.parent.utxo_list.update() self.main_window.utxo_list.update()
self.parent.update_completions() self.main_window.update_completions()

71
electrum/gui/qt/channels_list.py

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import traceback import traceback
import enum import enum
from typing import Sequence, Optional, Dict from typing import Sequence, Optional, Dict, TYPE_CHECKING
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
@ -24,6 +24,9 @@ from .util import (MyTreeView, WindowModalDialog, Buttons, OkButton, CancelButto
from .amountedit import BTCAmountEdit, FreezableLineEdit from .amountedit import BTCAmountEdit, FreezableLineEdit
from .util import read_QIcon, font_height from .util import read_QIcon, font_height
if TYPE_CHECKING:
from .main_window import ElectrumWindow
ROLE_CHANNEL_ID = Qt.UserRole ROLE_CHANNEL_ID = Qt.UserRole
@ -63,16 +66,18 @@ class ChannelsList(MyTreeView):
_default_item_bg_brush = None # type: Optional[QBrush] _default_item_bg_brush = None # type: Optional[QBrush]
def __init__(self, parent): def __init__(self, main_window: 'ElectrumWindow'):
super().__init__(parent, self.create_menu, stretch_column=self.Columns.NODE_ALIAS) super().__init__(
main_window=main_window,
stretch_column=self.Columns.NODE_ALIAS,
)
self.setModel(QtGui.QStandardItemModel(self)) self.setModel(QtGui.QStandardItemModel(self))
self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.main_window = parent
self.gossip_db_loaded.connect(self.on_gossip_db) self.gossip_db_loaded.connect(self.on_gossip_db)
self.update_rows.connect(self.do_update_rows) self.update_rows.connect(self.do_update_rows)
self.update_single_row.connect(self.do_update_single_row) self.update_single_row.connect(self.do_update_single_row)
self.network = self.parent.network self.network = self.main_window.network
self.wallet = self.parent.wallet self.wallet = self.main_window.wallet
self.setSortingEnabled(True) self.setSortingEnabled(True)
@property @property
@ -85,12 +90,12 @@ class ChannelsList(MyTreeView):
for subject in (REMOTE, LOCAL): for subject in (REMOTE, LOCAL):
if isinstance(chan, Channel): if isinstance(chan, Channel):
can_send = chan.available_to_spend(subject) / 1000 can_send = chan.available_to_spend(subject) / 1000
label = self.parent.format_amount(can_send, whitespaces=True) label = self.main_window.format_amount(can_send, whitespaces=True)
other = subject.inverted() other = subject.inverted()
bal_other = chan.balance(other)//1000 bal_other = chan.balance(other)//1000
bal_minus_htlcs_other = chan.balance_minus_outgoing_htlcs(other)//1000 bal_minus_htlcs_other = chan.balance_minus_outgoing_htlcs(other)//1000
if bal_other != bal_minus_htlcs_other: if bal_other != bal_minus_htlcs_other:
label += ' (+' + self.parent.format_amount(bal_other - bal_minus_htlcs_other, whitespaces=False) + ')' label += ' (+' + self.main_window.format_amount(bal_other - bal_minus_htlcs_other, whitespaces=False) + ')'
else: else:
assert isinstance(chan, ChannelBackup) assert isinstance(chan, ChannelBackup)
label = '' label = ''
@ -98,7 +103,7 @@ class ChannelsList(MyTreeView):
status = chan.get_state_for_GUI() status = chan.get_state_for_GUI()
closed = chan.is_closed() closed = chan.is_closed()
node_alias = self.lnworker.get_node_alias(chan.node_id) or chan.node_id.hex() node_alias = self.lnworker.get_node_alias(chan.node_id) or chan.node_id.hex()
capacity_str = self.parent.format_amount(chan.get_capacity(), whitespaces=True) capacity_str = self.main_window.format_amount(chan.get_capacity(), whitespaces=True)
return { return {
self.Columns.SHORT_CHANID: chan.short_id_for_GUI(), self.Columns.SHORT_CHANID: chan.short_id_for_GUI(),
self.Columns.LONG_CHANID: chan.channel_id.hex(), self.Columns.LONG_CHANID: chan.channel_id.hex(),
@ -125,7 +130,7 @@ class ChannelsList(MyTreeView):
self.is_force_close = False self.is_force_close = False
msg = _('Cooperative close?') msg = _('Cooperative close?')
msg += '\n' + _(messages.MSG_COOPERATIVE_CLOSE) msg += '\n' + _(messages.MSG_COOPERATIVE_CLOSE)
if not self.parent.question(msg): if not self.main_window.question(msg):
return return
coro = self.lnworker.close_channel(channel_id) coro = self.lnworker.close_channel(channel_id)
on_success = self.on_channel_closed on_success = self.on_channel_closed
@ -147,10 +152,10 @@ class ChannelsList(MyTreeView):
+ '<u>' + _('Please create a backup of your wallet file!') + '</u> '\ + '<u>' + _('Please create a backup of your wallet file!') + '</u> '\
+ '<p>' + _('Funds in this channel will not be recoverable from seed until they are swept back into your wallet, and might be lost if you lose your wallet file.') + ' '\ + '<p>' + _('Funds in this channel will not be recoverable from seed until they are swept back into your wallet, and might be lost if you lose your wallet file.') + ' '\
+ _('To prevent that, you should save a backup of your wallet on another device.') + '</p>' + _('To prevent that, you should save a backup of your wallet on another device.') + '</p>'
if not self.parent.question(msg, title=_('Force-close channel'), rich_text=True, checkbox=backup_cb): if not self.main_window.question(msg, title=_('Force-close channel'), rich_text=True, checkbox=backup_cb):
return return
if self.save_backup: if self.save_backup:
if not self.parent.backup_wallet(): if not self.main_window.backup_wallet():
return return
def task(): def task():
coro = self.lnworker.force_close_channel(channel_id) coro = self.lnworker.force_close_channel(channel_id)
@ -178,7 +183,7 @@ class ChannelsList(MyTreeView):
def request_force_close(self, channel_id): def request_force_close(self, channel_id):
msg = _('Request force-close from remote peer?') msg = _('Request force-close from remote peer?')
msg += '\n' + _(messages.MSG_REQUEST_FORCE_CLOSE) msg += '\n' + _(messages.MSG_REQUEST_FORCE_CLOSE)
if not self.parent.question(msg): if not self.main_window.question(msg):
return return
def task(): def task():
coro = self.lnworker.request_force_close(channel_id) coro = self.lnworker.request_force_close(channel_id)
@ -208,9 +213,9 @@ class ChannelsList(MyTreeView):
def on_rebalance(self): def on_rebalance(self):
chan1, chan2 = self.get_rebalance_pair() chan1, chan2 = self.get_rebalance_pair()
if chan1 is None: if chan1 is None:
self.parent.show_error("Select two active channels to rebalance.") self.main_window.show_error("Select two active channels to rebalance.")
return return
self.parent.rebalance_dialog(chan1, chan2) self.main_window.rebalance_dialog(chan1, chan2)
def create_menu(self, position): def create_menu(self, position):
menu = QMenu() menu = QMenu()
@ -222,7 +227,7 @@ class ChannelsList(MyTreeView):
if len(selected) == 2: if len(selected) == 2:
chan1, chan2 = self.get_rebalance_pair() chan1, chan2 = self.get_rebalance_pair()
if chan1 and chan2: if chan1 and chan2:
menu.addAction(_("Rebalance channels"), lambda: self.parent.rebalance_dialog(chan1, chan2)) menu.addAction(_("Rebalance channels"), lambda: self.main_window.rebalance_dialog(chan1, chan2))
menu.exec_(self.viewport().mapToGlobal(position)) menu.exec_(self.viewport().mapToGlobal(position))
return return
elif len(selected) > 2: elif len(selected) > 2:
@ -235,7 +240,7 @@ class ChannelsList(MyTreeView):
return return
channel_id = idx.sibling(idx.row(), self.Columns.NODE_ALIAS).data(ROLE_CHANNEL_ID) channel_id = idx.sibling(idx.row(), self.Columns.NODE_ALIAS).data(ROLE_CHANNEL_ID)
chan = self.lnworker.get_channel_by_id(channel_id) or self.lnworker.channel_backups[channel_id] chan = self.lnworker.get_channel_by_id(channel_id) or self.lnworker.channel_backups[channel_id]
menu.addAction(_("Details..."), lambda: self.parent.show_channel_details(chan)) menu.addAction(_("Details..."), lambda: self.main_window.show_channel_details(chan))
menu.addSeparator() menu.addSeparator()
cc = self.add_copy_menu(menu, idx) cc = self.add_copy_menu(menu, idx)
cc.addAction(_("Node ID"), lambda: self.place_text_on_clipboard( cc.addAction(_("Node ID"), lambda: self.place_text_on_clipboard(
@ -272,7 +277,7 @@ class ChannelsList(MyTreeView):
@QtCore.pyqtSlot(Abstract_Wallet, AbstractChannel) @QtCore.pyqtSlot(Abstract_Wallet, AbstractChannel)
def do_update_single_row(self, wallet: Abstract_Wallet, chan: AbstractChannel): def do_update_single_row(self, wallet: Abstract_Wallet, chan: AbstractChannel):
if wallet != self.parent.wallet: if wallet != self.wallet:
return return
for row in range(self.model().rowCount()): for row in range(self.model().rowCount()):
item = self.model().item(row, self.Columns.NODE_ALIAS) item = self.model().item(row, self.Columns.NODE_ALIAS)
@ -287,11 +292,11 @@ class ChannelsList(MyTreeView):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def on_gossip_db(self): def on_gossip_db(self):
self.do_update_rows(self.parent.wallet) self.do_update_rows(self.wallet)
@QtCore.pyqtSlot(Abstract_Wallet) @QtCore.pyqtSlot(Abstract_Wallet)
def do_update_rows(self, wallet): def do_update_rows(self, wallet):
if wallet != self.parent.wallet: if wallet != self.wallet:
return return
self.model().clear() self.model().clear()
self.update_headers(self.headers) self.update_headers(self.headers)
@ -337,29 +342,29 @@ class ChannelsList(MyTreeView):
item.setToolTip("") item.setToolTip("")
def update_can_send(self, lnworker: LNWallet): def update_can_send(self, lnworker: LNWallet):
msg = _('Can send') + ' ' + self.parent.format_amount(lnworker.num_sats_can_send())\ msg = _('Can send') + ' ' + self.main_window.format_amount(lnworker.num_sats_can_send())\
+ ' ' + self.parent.base_unit() + '; '\ + ' ' + self.main_window.base_unit() + '; '\
+ _('can receive') + ' ' + self.parent.format_amount(lnworker.num_sats_can_receive())\ + _('can receive') + ' ' + self.main_window.format_amount(lnworker.num_sats_can_receive())\
+ ' ' + self.parent.base_unit() + ' ' + self.main_window.base_unit()
self.can_send_label.setText(msg) self.can_send_label.setText(msg)
def create_toolbar(self, config): def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu('') toolbar, menu = self.create_toolbar_with_menu('')
self.can_send_label = toolbar.itemAt(0).widget() self.can_send_label = toolbar.itemAt(0).widget()
menu.addAction(_('Rebalance channels'), lambda: self.on_rebalance()) menu.addAction(_('Rebalance channels'), lambda: self.on_rebalance())
menu.addAction(_('Submarine swap'), lambda: self.parent.run_swap_dialog()) menu.addAction(_('Submarine swap'), lambda: self.main_window.run_swap_dialog())
menu.addSeparator() menu.addSeparator()
menu.addAction(_("Import channel backup"), lambda: self.parent.do_process_from_text_channel_backup()) menu.addAction(_("Import channel backup"), lambda: self.main_window.do_process_from_text_channel_backup())
self.new_channel_button = EnterButton(_('New Channel'), self.new_channel_with_warning) self.new_channel_button = EnterButton(_('New Channel'), self.new_channel_with_warning)
self.new_channel_button.setEnabled(self.parent.wallet.has_lightning()) self.new_channel_button.setEnabled(self.wallet.has_lightning())
toolbar.insertWidget(2, self.new_channel_button) toolbar.insertWidget(2, self.new_channel_button)
return toolbar return toolbar
def new_channel_with_warning(self): def new_channel_with_warning(self):
lnworker = self.parent.wallet.lnworker lnworker = self.wallet.lnworker
if not lnworker.channels and not lnworker.channel_backups: if not lnworker.channels and not lnworker.channel_backups:
warning = _(messages.MSG_LIGHTNING_WARNING) warning = _(messages.MSG_LIGHTNING_WARNING)
answer = self.parent.question( answer = self.main_window.question(
_('Do you want to create your first channel?') + '\n\n' + warning) _('Do you want to create your first channel?') + '\n\n' + warning)
if answer: if answer:
self.new_channel_dialog() self.new_channel_dialog()
@ -367,9 +372,9 @@ class ChannelsList(MyTreeView):
self.new_channel_dialog() self.new_channel_dialog()
def statistics_dialog(self): def statistics_dialog(self):
channel_db = self.parent.network.channel_db channel_db = self.network.channel_db
capacity = self.parent.format_amount(channel_db.capacity()) + ' '+ self.parent.base_unit() capacity = self.main_window.format_amount(channel_db.capacity()) + ' '+ self.main_window.base_unit()
d = WindowModalDialog(self.parent, _('Lightning Network Statistics')) d = WindowModalDialog(self.main_window, _('Lightning Network Statistics'))
d.setMinimumWidth(400) d.setMinimumWidth(400)
vbox = QVBoxLayout(d) vbox = QVBoxLayout(d)
h = QGridLayout() h = QGridLayout()
@ -385,7 +390,7 @@ class ChannelsList(MyTreeView):
def new_channel_dialog(self, *, amount_sat=None, min_amount_sat=None): def new_channel_dialog(self, *, amount_sat=None, min_amount_sat=None):
from .new_channel_dialog import NewChannelDialog from .new_channel_dialog import NewChannelDialog
d = NewChannelDialog(self.parent, amount_sat, min_amount_sat) d = NewChannelDialog(self.main_window, amount_sat, min_amount_sat)
return d.run() return d.run()
def set_visibility_of_columns(self): def set_visibility_of_columns(self):

32
electrum/gui/qt/contact_list.py

@ -24,6 +24,7 @@
# SOFTWARE. # SOFTWARE.
import enum import enum
from typing import TYPE_CHECKING
from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtCore import Qt, QPersistentModelIndex, QModelIndex from PyQt5.QtCore import Qt, QPersistentModelIndex, QModelIndex
@ -36,6 +37,9 @@ from electrum.plugin import run_hook
from .util import MyTreeView, webopen from .util import MyTreeView, webopen
if TYPE_CHECKING:
from .main_window import ElectrumWindow
class ContactList(MyTreeView): class ContactList(MyTreeView):
@ -52,10 +56,12 @@ class ContactList(MyTreeView):
ROLE_CONTACT_KEY = Qt.UserRole + 1000 ROLE_CONTACT_KEY = Qt.UserRole + 1000
key_role = ROLE_CONTACT_KEY key_role = ROLE_CONTACT_KEY
def __init__(self, parent): def __init__(self, main_window: 'ElectrumWindow'):
super().__init__(parent, self.create_menu, super().__init__(
stretch_column=self.Columns.NAME, main_window=main_window,
editable_columns=[self.Columns.NAME]) stretch_column=self.Columns.NAME,
editable_columns=[self.Columns.NAME],
)
self.setModel(QStandardItemModel(self)) self.setModel(QStandardItemModel(self))
self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setSortingEnabled(True) self.setSortingEnabled(True)
@ -63,8 +69,8 @@ class ContactList(MyTreeView):
self.update() self.update()
def on_edited(self, idx, edit_key, *, text): def on_edited(self, idx, edit_key, *, text):
_type, prior_name = self.parent.contacts.pop(edit_key) _type, prior_name = self.main_window.contacts.pop(edit_key)
self.parent.set_contact(text, edit_key) self.main_window.set_contact(text, edit_key)
self.update() self.update()
def create_menu(self, position): def create_menu(self, position):
@ -86,8 +92,8 @@ class ContactList(MyTreeView):
# would not be editable if openalias # would not be editable if openalias
persistent = QPersistentModelIndex(idx) persistent = QPersistentModelIndex(idx)
menu.addAction(_("Edit {}").format(column_title), lambda p=persistent: self.edit(QModelIndex(p))) menu.addAction(_("Edit {}").format(column_title), lambda p=persistent: self.edit(QModelIndex(p)))
menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(selected_keys)) menu.addAction(_("Pay to"), lambda: self.main_window.payto_contacts(selected_keys))
menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(selected_keys)) menu.addAction(_("Delete"), lambda: self.main_window.delete_contacts(selected_keys))
URLs = [block_explorer_URL(self.config, 'addr', key) for key in filter(is_address, selected_keys)] URLs = [block_explorer_URL(self.config, 'addr', key) for key in filter(is_address, selected_keys)]
if URLs: if URLs:
menu.addAction(_("View on block explorer"), lambda: [webopen(u) for u in URLs]) menu.addAction(_("View on block explorer"), lambda: [webopen(u) for u in URLs])
@ -102,8 +108,8 @@ class ContactList(MyTreeView):
self.model().clear() self.model().clear()
self.update_headers(self.__class__.headers) self.update_headers(self.__class__.headers)
set_current = None set_current = None
for key in sorted(self.parent.contacts.keys()): for key in sorted(self.main_window.contacts.keys()):
contact_type, name = self.parent.contacts[key] contact_type, name = self.main_window.contacts[key]
items = [QStandardItem(x) for x in (name, key)] items = [QStandardItem(x) for x in (name, key)]
items[self.Columns.NAME].setEditable(contact_type != 'openalias') items[self.Columns.NAME].setEditable(contact_type != 'openalias')
items[self.Columns.ADDRESS].setEditable(False) items[self.Columns.ADDRESS].setEditable(False)
@ -130,7 +136,7 @@ class ContactList(MyTreeView):
def create_toolbar(self, config): def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu('') toolbar, menu = self.create_toolbar_with_menu('')
menu.addAction(_("&New contact"), self.parent.new_contact_dialog) menu.addAction(_("&New contact"), self.main_window.new_contact_dialog)
menu.addAction(_("Import"), lambda: self.parent.import_contacts()) menu.addAction(_("Import"), lambda: self.main_window.import_contacts())
menu.addAction(_("Export"), lambda: self.parent.export_contacts()) menu.addAction(_("Export"), lambda: self.main_window.export_contacts())
return toolbar return toolbar

70
electrum/gui/qt/history_list.py

@ -486,12 +486,12 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
return True return True
return False return False
def __init__(self, parent, model: HistoryModel): def __init__(self, main_window: 'ElectrumWindow', model: HistoryModel):
super().__init__(parent, self.create_menu, super().__init__(
stretch_column=HistoryColumns.DESCRIPTION, main_window=main_window,
editable_columns=[HistoryColumns.DESCRIPTION, HistoryColumns.FIAT_VALUE]) stretch_column=HistoryColumns.DESCRIPTION,
self.main_window = parent editable_columns=[HistoryColumns.DESCRIPTION, HistoryColumns.FIAT_VALUE],
self.config = parent.config )
self.hm = model self.hm = model
self.proxy = HistorySortModel(self) self.proxy = HistorySortModel(self)
self.proxy.setSourceModel(model) self.proxy.setSourceModel(model)
@ -510,7 +510,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
self.end_button.setEnabled(False) self.end_button.setEnabled(False)
self.period_combo.addItems([_('All'), _('Custom')]) self.period_combo.addItems([_('All'), _('Custom')])
self.period_combo.activated.connect(self.on_combo) self.period_combo.activated.connect(self.on_combo)
self.wallet = self.parent.wallet # type: Abstract_Wallet self.wallet = self.main_window.wallet # type: Abstract_Wallet
self.sortByColumn(HistoryColumns.STATUS, Qt.AscendingOrder) self.sortByColumn(HistoryColumns.STATUS, Qt.AscendingOrder)
self.setRootIsDecorated(True) self.setRootIsDecorated(True)
self.header().setStretchLastSection(False) self.header().setStretchLastSection(False)
@ -603,24 +603,24 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
def show_summary(self): def show_summary(self):
if not self.hm.should_show_fiat(): if not self.hm.should_show_fiat():
self.parent.show_message(_("Enable fiat exchange rate with history.")) self.main_window.show_message(_("Enable fiat exchange rate with history."))
return return
fx = self.parent.fx fx = self.main_window.fx
h = self.wallet.get_detailed_history( h = self.wallet.get_detailed_history(
from_timestamp = time.mktime(self.start_date.timetuple()) if self.start_date else None, from_timestamp = time.mktime(self.start_date.timetuple()) if self.start_date else None,
to_timestamp = time.mktime(self.end_date.timetuple()) if self.end_date else None, to_timestamp = time.mktime(self.end_date.timetuple()) if self.end_date else None,
fx=fx) fx=fx)
summary = h['summary'] summary = h['summary']
if not summary: if not summary:
self.parent.show_message(_("Nothing to summarize.")) self.main_window.show_message(_("Nothing to summarize."))
return return
start = summary['begin'] start = summary['begin']
end = summary['end'] end = summary['end']
flow = summary['flow'] flow = summary['flow']
start_date = start.get('date') start_date = start.get('date')
end_date = end.get('date') end_date = end.get('date')
format_amount = lambda x: self.parent.format_amount(x.value) + ' ' + self.parent.base_unit() format_amount = lambda x: self.main_window.format_amount(x.value) + ' ' + self.main_window.base_unit()
format_fiat = lambda x: str(x) + ' ' + self.parent.fx.ccy format_fiat = lambda x: str(x) + ' ' + self.main_window.fx.ccy
d = WindowModalDialog(self, _("Summary")) d = WindowModalDialog(self, _("Summary"))
d.setMinimumSize(600, 150) d.setMinimumSize(600, 150)
@ -679,7 +679,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
from electrum.plot import plot_history, NothingToPlotException from electrum.plot import plot_history, NothingToPlotException
except Exception as e: except Exception as e:
_logger.error(f"could not import electrum.plot. This feature needs matplotlib to be installed. exc={e!r}") _logger.error(f"could not import electrum.plot. This feature needs matplotlib to be installed. exc={e!r}")
self.parent.show_message( self.main_window.show_message(
_("Can't plot history.") + '\n' + _("Can't plot history.") + '\n' +
_("Perhaps some dependencies are missing...") + " (matplotlib?)" + '\n' + _("Perhaps some dependencies are missing...") + " (matplotlib?)" + '\n' +
f"Error: {e!r}" f"Error: {e!r}"
@ -689,7 +689,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
plt = plot_history(list(self.hm.transactions.values())) plt = plot_history(list(self.hm.transactions.values()))
plt.show() plt.show()
except NothingToPlotException as e: except NothingToPlotException as e:
self.parent.show_message(str(e)) self.main_window.show_message(str(e))
def on_edited(self, idx, edit_key, *, text): def on_edited(self, idx, edit_key, *, text):
index = self.model().mapToSource(idx) index = self.model().mapToSource(idx)
@ -699,9 +699,9 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
if column == HistoryColumns.DESCRIPTION: if column == HistoryColumns.DESCRIPTION:
if self.wallet.set_label(key, text): #changed if self.wallet.set_label(key, text): #changed
self.hm.update_label(index) self.hm.update_label(index)
self.parent.update_completions() self.main_window.update_completions()
elif column == HistoryColumns.FIAT_VALUE: elif column == HistoryColumns.FIAT_VALUE:
self.wallet.set_fiat_value(key, self.parent.fx.ccy, text, self.parent.fx, tx_item['value'].value) self.wallet.set_fiat_value(key, self.main_window.fx.ccy, text, self.main_window.fx, tx_item['value'].value)
value = tx_item['value'].value value = tx_item['value'].value
if value is not None: if value is not None:
self.hm.update_fiat(index) self.hm.update_fiat(index)
@ -720,13 +720,13 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
else: else:
if tx_item.get('lightning'): if tx_item.get('lightning'):
if tx_item['type'] == 'payment': if tx_item['type'] == 'payment':
self.parent.show_lightning_transaction(tx_item) self.main_window.show_lightning_transaction(tx_item)
return return
tx_hash = tx_item['txid'] tx_hash = tx_item['txid']
tx = self.wallet.adb.get_transaction(tx_hash) tx = self.wallet.adb.get_transaction(tx_hash)
if not tx: if not tx:
return return
self.parent.show_transaction(tx) self.main_window.show_transaction(tx)
def add_copy_menu(self, menu, idx): def add_copy_menu(self, menu, idx):
cc = menu.addMenu(_("Copy")) cc = menu.addMenu(_("Copy"))
@ -751,14 +751,14 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
tx_item = idx.internalPointer().get_data() tx_item = idx.internalPointer().get_data()
if tx_item.get('lightning') and tx_item['type'] == 'payment': if tx_item.get('lightning') and tx_item['type'] == 'payment':
menu = QMenu() menu = QMenu()
menu.addAction(_("View Payment"), lambda: self.parent.show_lightning_transaction(tx_item)) menu.addAction(_("View Payment"), lambda: self.main_window.show_lightning_transaction(tx_item))
cc = self.add_copy_menu(menu, idx) cc = self.add_copy_menu(menu, idx)
cc.addAction(_("Payment Hash"), lambda: self.place_text_on_clipboard(tx_item['payment_hash'], title="Payment Hash")) cc.addAction(_("Payment Hash"), lambda: self.place_text_on_clipboard(tx_item['payment_hash'], title="Payment Hash"))
cc.addAction(_("Preimage"), lambda: self.place_text_on_clipboard(tx_item['preimage'], title="Preimage")) cc.addAction(_("Preimage"), lambda: self.place_text_on_clipboard(tx_item['preimage'], title="Preimage"))
key = tx_item['payment_hash'] key = tx_item['payment_hash']
log = self.wallet.lnworker.logs.get(key) log = self.wallet.lnworker.logs.get(key)
if log: if log:
menu.addAction(_("View log"), lambda: self.parent.send_tab.invoice_list.show_log(key, log)) menu.addAction(_("View log"), lambda: self.main_window.send_tab.invoice_list.show_log(key, log))
menu.exec_(self.viewport().mapToGlobal(position)) menu.exec_(self.viewport().mapToGlobal(position))
return return
tx_hash = tx_item['txid'] tx_hash = tx_item['txid']
@ -780,25 +780,25 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
# TODO use siblingAtColumn when min Qt version is >=5.11 # TODO use siblingAtColumn when min Qt version is >=5.11
persistent = QPersistentModelIndex(org_idx.sibling(org_idx.row(), c)) persistent = QPersistentModelIndex(org_idx.sibling(org_idx.row(), c))
menu_edit.addAction(_("{}").format(label), lambda p=persistent: self.edit(QModelIndex(p))) menu_edit.addAction(_("{}").format(label), lambda p=persistent: self.edit(QModelIndex(p)))
menu.addAction(_("View Transaction"), lambda: self.parent.show_transaction(tx)) menu.addAction(_("View Transaction"), lambda: self.main_window.show_transaction(tx))
channel_id = tx_item.get('channel_id') channel_id = tx_item.get('channel_id')
if channel_id and self.wallet.lnworker and (chan := self.wallet.lnworker.get_channel_by_id(bytes.fromhex(channel_id))): if channel_id and self.wallet.lnworker and (chan := self.wallet.lnworker.get_channel_by_id(bytes.fromhex(channel_id))):
menu.addAction(_("View Channel"), lambda: self.parent.show_channel_details(chan)) menu.addAction(_("View Channel"), lambda: self.main_window.show_channel_details(chan))
if is_unconfirmed and tx: if is_unconfirmed and tx:
if tx_details.can_bump: if tx_details.can_bump:
menu.addAction(_("Increase fee"), lambda: self.parent.bump_fee_dialog(tx)) menu.addAction(_("Increase fee"), lambda: self.main_window.bump_fee_dialog(tx))
else: else:
if tx_details.can_cpfp: if tx_details.can_cpfp:
menu.addAction(_("Child pays for parent"), lambda: self.parent.cpfp_dialog(tx)) menu.addAction(_("Child pays for parent"), lambda: self.main_window.cpfp_dialog(tx))
if tx_details.can_dscancel: if tx_details.can_dscancel:
menu.addAction(_("Cancel (double-spend)"), lambda: self.parent.dscancel_dialog(tx)) menu.addAction(_("Cancel (double-spend)"), lambda: self.main_window.dscancel_dialog(tx))
invoices = self.wallet.get_relevant_invoices_for_tx(tx_hash) invoices = self.wallet.get_relevant_invoices_for_tx(tx_hash)
if len(invoices) == 1: if len(invoices) == 1:
menu.addAction(_("View invoice"), lambda inv=invoices[0]: self.parent.show_onchain_invoice(inv)) menu.addAction(_("View invoice"), lambda inv=invoices[0]: self.main_window.show_onchain_invoice(inv))
elif len(invoices) > 1: elif len(invoices) > 1:
menu_invs = menu.addMenu(_("Related invoices")) menu_invs = menu.addMenu(_("Related invoices"))
for inv in invoices: for inv in invoices:
menu_invs.addAction(_("View invoice"), lambda inv=inv: self.parent.show_onchain_invoice(inv)) menu_invs.addAction(_("View invoice"), lambda inv=inv: self.main_window.show_onchain_invoice(inv))
if tx_URL: if tx_URL:
menu.addAction(_("View on block explorer"), lambda: webopen(tx_URL)) menu.addAction(_("View on block explorer"), lambda: webopen(tx_URL))
menu.exec_(self.viewport().mapToGlobal(position)) menu.exec_(self.viewport().mapToGlobal(position))
@ -809,24 +809,24 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
if num_child_txs > 0: if num_child_txs > 0:
question = (_("Are you sure you want to remove this transaction and {} child transactions?") question = (_("Are you sure you want to remove this transaction and {} child transactions?")
.format(num_child_txs)) .format(num_child_txs))
if not self.parent.question(msg=question, if not self.main_window.question(msg=question,
title=_("Please confirm")): title=_("Please confirm")):
return return
self.wallet.adb.remove_transaction(tx_hash) self.wallet.adb.remove_transaction(tx_hash)
self.wallet.save_db() self.wallet.save_db()
# need to update at least: history_list, utxo_list, address_list # need to update at least: history_list, utxo_list, address_list
self.parent.need_update.set() self.main_window.need_update.set()
def onFileAdded(self, fn): def onFileAdded(self, fn):
try: try:
with open(fn) as f: with open(fn) as f:
tx = self.parent.tx_from_text(f.read()) tx = self.main_window.tx_from_text(f.read())
except IOError as e: except IOError as e:
self.parent.show_error(e) self.main_window.show_error(e)
return return
if not tx: if not tx:
return return
self.parent.save_transaction_into_wallet(tx) self.main_window.save_transaction_into_wallet(tx)
def export_history_dialog(self): def export_history_dialog(self):
d = WindowModalDialog(self, _('Export History')) d = WindowModalDialog(self, _('Export History'))
@ -850,12 +850,12 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
self.do_export_history(filename, csv_button.isChecked()) self.do_export_history(filename, csv_button.isChecked())
except (IOError, os.error) as reason: except (IOError, os.error) as reason:
export_error_label = _("Electrum was unable to produce a transaction export.") export_error_label = _("Electrum was unable to produce a transaction export.")
self.parent.show_critical(export_error_label + "\n" + str(reason), title=_("Unable to export history")) self.main_window.show_critical(export_error_label + "\n" + str(reason), title=_("Unable to export history"))
return return
self.parent.show_message(_("Your wallet history has been successfully exported.")) self.main_window.show_message(_("Your wallet history has been successfully exported."))
def do_export_history(self, file_name, is_csv): def do_export_history(self, file_name, is_csv):
hist = self.wallet.get_detailed_history(fx=self.parent.fx) hist = self.wallet.get_detailed_history(fx=self.main_window.fx)
txns = hist['transactions'] txns = hist['transactions']
lines = [] lines = []
if is_csv: if is_csv:

14
electrum/gui/qt/invoice_list.py

@ -69,8 +69,10 @@ class InvoiceList(MyTreeView):
def __init__(self, send_tab: 'SendTab'): def __init__(self, send_tab: 'SendTab'):
window = send_tab.window window = send_tab.window
super().__init__(window, self.create_menu, super().__init__(
stretch_column=self.Columns.DESCRIPTION) main_window=window,
stretch_column=self.Columns.DESCRIPTION,
)
self.wallet = window.wallet self.wallet = window.wallet
self.send_tab = send_tab self.send_tab = send_tab
self.std_model = QStandardItemModel(self) self.std_model = QStandardItemModel(self)
@ -115,7 +117,7 @@ class InvoiceList(MyTreeView):
labels = [""] * len(self.Columns) labels = [""] * len(self.Columns)
labels[self.Columns.DATE] = format_time(timestamp) if timestamp else _('Unknown') labels[self.Columns.DATE] = format_time(timestamp) if timestamp else _('Unknown')
labels[self.Columns.DESCRIPTION] = item.message labels[self.Columns.DESCRIPTION] = item.message
labels[self.Columns.AMOUNT] = self.parent.format_amount(amount, whitespaces=True) labels[self.Columns.AMOUNT] = self.main_window.format_amount(amount, whitespaces=True)
labels[self.Columns.STATUS] = item.get_status_str(status) labels[self.Columns.STATUS] = item.get_status_str(status)
items = [QStandardItem(e) for e in labels] items = [QStandardItem(e) for e in labels]
self.set_editability(items) self.set_editability(items)
@ -160,11 +162,11 @@ class InvoiceList(MyTreeView):
copy_menu = self.add_copy_menu(menu, idx) copy_menu = self.add_copy_menu(menu, idx)
address = invoice.get_address() address = invoice.get_address()
if address: if address:
copy_menu.addAction(_("Address"), lambda: self.parent.do_copy(invoice.get_address(), title='Bitcoin Address')) copy_menu.addAction(_("Address"), lambda: self.main_window.do_copy(invoice.get_address(), title='Bitcoin Address'))
if invoice.is_lightning(): if invoice.is_lightning():
menu.addAction(_("Details"), lambda: self.parent.show_lightning_invoice(invoice)) menu.addAction(_("Details"), lambda: self.main_window.show_lightning_invoice(invoice))
else: else:
menu.addAction(_("Details"), lambda: self.parent.show_onchain_invoice(invoice)) menu.addAction(_("Details"), lambda: self.main_window.show_onchain_invoice(invoice))
status = wallet.get_invoice_status(invoice) status = wallet.get_invoice_status(invoice)
if status == PR_UNPAID: if status == PR_UNPAID:
menu.addAction(_("Pay") + "...", lambda: self.send_tab.do_pay_invoice(invoice)) menu.addAction(_("Pay") + "...", lambda: self.send_tab.do_pay_invoice(invoice))

16
electrum/gui/qt/request_list.py

@ -73,8 +73,10 @@ class RequestList(MyTreeView):
def __init__(self, receive_tab: 'ReceiveTab'): def __init__(self, receive_tab: 'ReceiveTab'):
window = receive_tab.window window = receive_tab.window
super().__init__(window, self.create_menu, super().__init__(
stretch_column=self.Columns.DESCRIPTION) main_window=window,
stretch_column=self.Columns.DESCRIPTION,
)
self.wallet = window.wallet self.wallet = window.wallet
self.receive_tab = receive_tab self.receive_tab = receive_tab
self.std_model = QStandardItemModel(self) self.std_model = QStandardItemModel(self)
@ -141,7 +143,7 @@ class RequestList(MyTreeView):
amount = req.get_amount_sat() amount = req.get_amount_sat()
message = req.get_message() message = req.get_message()
date = format_time(timestamp) date = format_time(timestamp)
amount_str = self.parent.format_amount(amount) if amount else "" amount_str = self.main_window.format_amount(amount) if amount else ""
labels = [""] * len(self.Columns) labels = [""] * len(self.Columns)
labels[self.Columns.DATE] = date labels[self.Columns.DATE] = date
labels[self.Columns.DESCRIPTION] = message labels[self.Columns.DESCRIPTION] = message
@ -193,15 +195,15 @@ class RequestList(MyTreeView):
menu = QMenu(self) menu = QMenu(self)
copy_menu = self.add_copy_menu(menu, idx) copy_menu = self.add_copy_menu(menu, idx)
if req.get_address(): if req.get_address():
copy_menu.addAction(_("Address"), lambda: self.parent.do_copy(req.get_address(), title='Bitcoin Address')) copy_menu.addAction(_("Address"), lambda: self.main_window.do_copy(req.get_address(), title='Bitcoin Address'))
if URI := self.wallet.get_request_URI(req): if URI := self.wallet.get_request_URI(req):
copy_menu.addAction(_("Bitcoin URI"), lambda: self.parent.do_copy(URI, title='Bitcoin URI')) copy_menu.addAction(_("Bitcoin URI"), lambda: self.main_window.do_copy(URI, title='Bitcoin URI'))
if req.is_lightning(): if req.is_lightning():
copy_menu.addAction(_("Lightning Request"), lambda: self.parent.do_copy(self.wallet.get_bolt11_invoice(req), title='Lightning Request')) copy_menu.addAction(_("Lightning Request"), lambda: self.main_window.do_copy(self.wallet.get_bolt11_invoice(req), title='Lightning Request'))
#if 'view_url' in req: #if 'view_url' in req:
# menu.addAction(_("View in web browser"), lambda: webopen(req['view_url'])) # menu.addAction(_("View in web browser"), lambda: webopen(req['view_url']))
menu.addAction(_("Delete"), lambda: self.delete_requests([key])) menu.addAction(_("Delete"), lambda: self.delete_requests([key]))
run_hook('receive_list_menu', self.parent, menu, key) run_hook('receive_list_menu', self.main_window, menu, key)
menu.exec_(self.viewport().mapToGlobal(position)) menu.exec_(self.viewport().mapToGlobal(position))
def delete_requests(self, keys): def delete_requests(self, keys):

22
electrum/gui/qt/util.py

@ -626,14 +626,21 @@ class MyTreeView(QTreeView):
Columns: Type[BaseColumnsEnum] Columns: Type[BaseColumnsEnum]
def __init__(self, parent: 'ElectrumWindow', create_menu, *, def __init__(
stretch_column=None, editable_columns=None): self,
*,
parent: Optional[QWidget] = None,
main_window: Optional['ElectrumWindow'] = None,
stretch_column: Optional[int] = None,
editable_columns: Optional[Sequence[int]] = None,
):
parent = parent or main_window
super().__init__(parent) super().__init__(parent)
self.parent = parent self.main_window = main_window
self.config = self.parent.config self.config = self.main_window.config if self.main_window else None
self.stretch_column = stretch_column self.stretch_column = stretch_column
self.setContextMenuPolicy(Qt.CustomContextMenu) self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(create_menu) self.customContextMenuRequested.connect(self.create_menu)
self.setUniformRowHeights(True) self.setUniformRowHeights(True)
# Control which columns are editable # Control which columns are editable
@ -659,6 +666,9 @@ class MyTreeView(QTreeView):
self._default_bg_brush = QStandardItem().background() self._default_bg_brush = QStandardItem().background()
def create_menu(self, position: QPoint) -> None:
pass
def set_editability(self, items): def set_editability(self, items):
for idx, i in enumerate(items): for idx, i in enumerate(items):
i.setEditable(idx in self.editable_columns) i.setEditable(idx in self.editable_columns)
@ -836,7 +846,7 @@ class MyTreeView(QTreeView):
return cc return cc
def place_text_on_clipboard(self, text: str, *, title: str = None) -> None: def place_text_on_clipboard(self, text: str, *, title: str = None) -> None:
self.parent.do_copy(text, title=title) self.main_window.do_copy(text, title=title)
def showEvent(self, e: 'QShowEvent'): def showEvent(self, e: 'QShowEvent'):
super().showEvent(e) super().showEvent(e)

53
electrum/gui/qt/utxo_list.py

@ -23,7 +23,7 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
from typing import Optional, List, Dict, Sequence, Set from typing import Optional, List, Dict, Sequence, Set, TYPE_CHECKING
import enum import enum
import copy import copy
@ -39,6 +39,9 @@ from electrum.lnutil import LN_MAX_FUNDING_SAT, MIN_FUNDING_SAT
from .util import MyTreeView, ColorScheme, MONOSPACE_FONT, EnterButton from .util import MyTreeView, ColorScheme, MONOSPACE_FONT, EnterButton
from .new_channel_dialog import NewChannelDialog from .new_channel_dialog import NewChannelDialog
if TYPE_CHECKING:
from .main_window import ElectrumWindow
class UTXOList(MyTreeView): class UTXOList(MyTreeView):
_spend_set: Set[str] # coins selected by the user to spend from _spend_set: Set[str] # coins selected by the user to spend from
@ -64,12 +67,14 @@ class UTXOList(MyTreeView):
ROLE_PREVOUT_STR = Qt.UserRole + 1000 ROLE_PREVOUT_STR = Qt.UserRole + 1000
key_role = ROLE_PREVOUT_STR key_role = ROLE_PREVOUT_STR
def __init__(self, parent): def __init__(self, main_window: 'ElectrumWindow'):
super().__init__(parent, self.create_menu, super().__init__(
stretch_column=self.stretch_column) main_window=main_window,
stretch_column=self.stretch_column,
)
self._spend_set = set() self._spend_set = set()
self._utxo_dict = {} self._utxo_dict = {}
self.wallet = self.parent.wallet self.wallet = self.main_window.wallet
self.std_model = QStandardItemModel(self) self.std_model = QStandardItemModel(self)
self.setModel(self.std_model) self.setModel(self.std_model)
self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setSelectionMode(QAbstractItemView.ExtendedSelection)
@ -95,7 +100,7 @@ class UTXOList(MyTreeView):
labels = [""] * len(self.Columns) labels = [""] * len(self.Columns)
labels[self.Columns.OUTPOINT] = str(utxo.short_id) labels[self.Columns.OUTPOINT] = str(utxo.short_id)
labels[self.Columns.ADDRESS] = utxo.address labels[self.Columns.ADDRESS] = utxo.address
labels[self.Columns.AMOUNT] = self.parent.format_amount(utxo.value_sats(), whitespaces=True) labels[self.Columns.AMOUNT] = self.main_window.format_amount(utxo.value_sats(), whitespaces=True)
utxo_item = [QStandardItem(x) for x in labels] utxo_item = [QStandardItem(x) for x in labels]
self.set_editability(utxo_item) self.set_editability(utxo_item)
utxo_item[self.Columns.OUTPOINT].setData(name, self.ROLE_PREVOUT_STR) utxo_item[self.Columns.OUTPOINT].setData(name, self.ROLE_PREVOUT_STR)
@ -115,11 +120,11 @@ class UTXOList(MyTreeView):
coins = [self._utxo_dict[x] for x in self._spend_set] coins = [self._utxo_dict[x] for x in self._spend_set]
coins = self._filter_frozen_coins(coins) coins = self._filter_frozen_coins(coins)
amount = sum(x.value_sats() for x in coins) amount = sum(x.value_sats() for x in coins)
amount_str = self.parent.format_amount_and_units(amount) amount_str = self.main_window.format_amount_and_units(amount)
num_outputs_str = _("{} outputs available ({} total)").format(len(coins), len(self._utxo_dict)) num_outputs_str = _("{} outputs available ({} total)").format(len(coins), len(self._utxo_dict))
self.parent.set_coincontrol_msg(_("Coin control active") + f': {num_outputs_str}, {amount_str}') self.main_window.set_coincontrol_msg(_("Coin control active") + f': {num_outputs_str}, {amount_str}')
else: else:
self.parent.set_coincontrol_msg(None) self.main_window.set_coincontrol_msg(None)
def refresh_row(self, key, row): def refresh_row(self, key, row):
assert row is not None assert row is not None
@ -184,7 +189,7 @@ class UTXOList(MyTreeView):
selected = self.get_selected_outpoints() selected = self.get_selected_outpoints()
coins = [self._utxo_dict[name] for name in selected] coins = [self._utxo_dict[name] for name in selected]
if not coins: if not coins:
self.parent.show_error(_('You need to select coins from the list first.\nUse ctrl+left mouse button to select multiple items')) self.main_window.show_error(_('You need to select coins from the list first.\nUse ctrl+left mouse button to select multiple items'))
return return
self.add_to_coincontrol(coins) self.add_to_coincontrol(coins)
@ -223,7 +228,7 @@ class UTXOList(MyTreeView):
def swap_coins(self, coins): def swap_coins(self, coins):
#self.clear_coincontrol() #self.clear_coincontrol()
self.add_to_coincontrol(coins) self.add_to_coincontrol(coins)
self.parent.run_swap_dialog(is_reverse=False, recv_amount_sat='!') self.main_window.run_swap_dialog(is_reverse=False, recv_amount_sat='!')
self.clear_coincontrol() self.clear_coincontrol()
def can_open_channel(self, coins): def can_open_channel(self, coins):
@ -236,7 +241,7 @@ class UTXOList(MyTreeView):
# todo : use a single dialog in new flow # todo : use a single dialog in new flow
#self.clear_coincontrol() #self.clear_coincontrol()
self.add_to_coincontrol(coins) self.add_to_coincontrol(coins)
d = NewChannelDialog(self.parent) d = NewChannelDialog(self.main_window)
d.max_button.setChecked(True) d.max_button.setChecked(True)
d.max_button.setEnabled(False) d.max_button.setEnabled(False)
d.min_button.setEnabled(False) d.min_button.setEnabled(False)
@ -247,15 +252,15 @@ class UTXOList(MyTreeView):
self.clear_coincontrol() self.clear_coincontrol()
def clipboard_contains_address(self): def clipboard_contains_address(self):
text = self.parent.app.clipboard().text() text = self.main_window.app.clipboard().text()
return is_address(text) return is_address(text)
def pay_to_clipboard_address(self, coins): def pay_to_clipboard_address(self, coins):
addr = self.parent.app.clipboard().text() addr = self.main_window.app.clipboard().text()
outputs = [PartialTxOutput.from_address_and_value(addr, '!')] outputs = [PartialTxOutput.from_address_and_value(addr, '!')]
#self.clear_coincontrol() #self.clear_coincontrol()
self.add_to_coincontrol(coins) self.add_to_coincontrol(coins)
self.parent.send_tab.pay_onchain_dialog(outputs) self.main_window.send_tab.pay_onchain_dialog(outputs)
self.clear_coincontrol() self.clear_coincontrol()
def create_menu(self, position): def create_menu(self, position):
@ -277,7 +282,7 @@ class UTXOList(MyTreeView):
tx = self.wallet.adb.get_transaction(txid) tx = self.wallet.adb.get_transaction(txid)
if tx: if tx:
label = self.wallet.get_label_for_txid(txid) label = self.wallet.get_label_for_txid(txid)
menu.addAction(_("Privacy analysis"), lambda: self.parent.show_utxo(utxo)) menu.addAction(_("Privacy analysis"), lambda: self.main_window.show_utxo(utxo))
# fully spend # fully spend
menu_spend = menu.addMenu(_("Fully spend") + '') menu_spend = menu.addMenu(_("Fully spend") + '')
m = menu_spend.addAction(_("send to address in clipboard"), lambda: self.pay_to_clipboard_address(coins)) m = menu_spend.addAction(_("send to address in clipboard"), lambda: self.pay_to_clipboard_address(coins))
@ -297,13 +302,13 @@ class UTXOList(MyTreeView):
addr = utxo.address addr = utxo.address
menu_freeze = menu.addMenu(_("Freeze")) menu_freeze = menu.addMenu(_("Freeze"))
if not self.wallet.is_frozen_coin(utxo): if not self.wallet.is_frozen_coin(utxo):
menu_freeze.addAction(_("Freeze Coin"), lambda: self.parent.set_frozen_state_of_coins([utxo], True)) menu_freeze.addAction(_("Freeze Coin"), lambda: self.main_window.set_frozen_state_of_coins([utxo], True))
else: else:
menu_freeze.addAction(_("Unfreeze Coin"), lambda: self.parent.set_frozen_state_of_coins([utxo], False)) menu_freeze.addAction(_("Unfreeze Coin"), lambda: self.main_window.set_frozen_state_of_coins([utxo], False))
if not self.wallet.is_frozen_address(addr): if not self.wallet.is_frozen_address(addr):
menu_freeze.addAction(_("Freeze Address"), lambda: self.parent.set_frozen_state_of_addresses([addr], True)) menu_freeze.addAction(_("Freeze Address"), lambda: self.main_window.set_frozen_state_of_addresses([addr], True))
else: else:
menu_freeze.addAction(_("Unfreeze Address"), lambda: self.parent.set_frozen_state_of_addresses([addr], False)) menu_freeze.addAction(_("Unfreeze Address"), lambda: self.main_window.set_frozen_state_of_addresses([addr], False))
elif len(coins) > 1: # multiple items selected elif len(coins) > 1: # multiple items selected
menu.addSeparator() menu.addSeparator()
addrs = [utxo.address for utxo in coins] addrs = [utxo.address for utxo in coins]
@ -311,13 +316,13 @@ class UTXOList(MyTreeView):
is_addr_frozen = [self.wallet.is_frozen_address(utxo.address) for utxo in coins] is_addr_frozen = [self.wallet.is_frozen_address(utxo.address) for utxo in coins]
menu_freeze = menu.addMenu(_("Freeze")) menu_freeze = menu.addMenu(_("Freeze"))
if not all(is_coin_frozen): if not all(is_coin_frozen):
menu_freeze.addAction(_("Freeze Coins"), lambda: self.parent.set_frozen_state_of_coins(coins, True)) menu_freeze.addAction(_("Freeze Coins"), lambda: self.main_window.set_frozen_state_of_coins(coins, True))
if any(is_coin_frozen): if any(is_coin_frozen):
menu_freeze.addAction(_("Unfreeze Coins"), lambda: self.parent.set_frozen_state_of_coins(coins, False)) menu_freeze.addAction(_("Unfreeze Coins"), lambda: self.main_window.set_frozen_state_of_coins(coins, False))
if not all(is_addr_frozen): if not all(is_addr_frozen):
menu_freeze.addAction(_("Freeze Addresses"), lambda: self.parent.set_frozen_state_of_addresses(addrs, True)) menu_freeze.addAction(_("Freeze Addresses"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, True))
if any(is_addr_frozen): if any(is_addr_frozen):
menu_freeze.addAction(_("Unfreeze Addresses"), lambda: self.parent.set_frozen_state_of_addresses(addrs, False)) menu_freeze.addAction(_("Unfreeze Addresses"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, False))
menu.exec_(self.viewport().mapToGlobal(position)) menu.exec_(self.viewport().mapToGlobal(position))

11
electrum/gui/qt/watchtower_dialog.py

@ -32,15 +32,16 @@ from .util import MyTreeView, Buttons
class WatcherList(MyTreeView): class WatcherList(MyTreeView):
def __init__(self, parent): def __init__(self, parent: 'WatchtowerDialog'):
super().__init__(parent, self.create_menu, stretch_column=0) super().__init__(
parent=parent,
stretch_column=0,
)
self.parent = parent
self.setModel(QStandardItemModel(self)) self.setModel(QStandardItemModel(self))
self.setSortingEnabled(True) self.setSortingEnabled(True)
self.update() self.update()
def create_menu(self, x):
pass
def update(self): def update(self):
if self.parent.lnwatcher is None: if self.parent.lnwatcher is None:
return return

Loading…
Cancel
Save