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. 61
      electrum/gui/qt/address_list.py
  2. 71
      electrum/gui/qt/channels_list.py
  3. 30
      electrum/gui/qt/contact_list.py
  4. 68
      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

61
electrum/gui/qt/address_list.py

@ -25,6 +25,7 @@
import enum
from enum import IntEnum
from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, QPersistentModelIndex, QModelIndex
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
if TYPE_CHECKING:
from .main_window import ElectrumWindow
class AddressUsageStateFilter(IntEnum):
ALL = 0
@ -85,12 +89,13 @@ class AddressList(MyTreeView):
ROLE_ADDRESS_STR = Qt.UserRole + 1001
key_role = ROLE_ADDRESS_STR
def __init__(self, parent):
super().__init__(parent, self.create_menu,
def __init__(self, main_window: 'ElectrumWindow'):
super().__init__(
main_window=main_window,
stretch_column=self.Columns.LABEL,
editable_columns=[self.Columns.LABEL])
self.main_window = parent
self.wallet = self.parent.wallet
editable_columns=[self.Columns.LABEL],
)
self.wallet = self.main_window.wallet
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setSortingEnabled(True)
self.show_change = AddressTypeFilter.ALL # type: AddressTypeFilter
@ -120,7 +125,7 @@ class AddressList(MyTreeView):
return toolbar
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):
return self.change_button, self.used_button
@ -132,7 +137,7 @@ class AddressList(MyTreeView):
def refresh_headers(self):
if self.should_show_fiat():
ccy = self.parent.fx.get_currency()
ccy = self.main_window.fx.get_currency()
else:
ccy = _('Fiat')
headers = {
@ -171,7 +176,7 @@ class AddressList(MyTreeView):
self.proxy.setDynamicSortFilter(False) # temp. disable re-sorting after every change
self.std_model.clear()
self.refresh_headers()
fx = self.parent.fx
fx = self.main_window.fx
set_address = None
num_shown = 0
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)
c, u, x = self.wallet.get_addr_balance(address)
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
fx = self.parent.fx
fx = self.main_window.fx
if self.should_show_fiat():
rate = fx.exchange_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_idx = idx.sibling(idx.row(), self.Columns.LABEL)
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)
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():
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():
menu.addAction(_("Sign/verify message"), lambda: self.parent.sign_verify_message(addr))
menu.addAction(_("Encrypt/decrypt message"), lambda: self.parent.encrypt_message(addr))
menu.addAction(_("Sign/verify message"), lambda: self.main_window.sign_verify_message(addr))
menu.addAction(_("Encrypt/decrypt message"), lambda: self.main_window.encrypt_message(addr))
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)
if addr_URL:
menu.addAction(_("View on block explorer"), lambda: webopen(addr_URL))
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:
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:
# multiple items selected
menu.addAction(_("Freeze"), lambda: self.parent.set_frozen_state_of_addresses(addrs, True))
menu.addAction(_("Unfreeze"), lambda: self.parent.set_frozen_state_of_addresses(addrs, False))
menu.addAction(_("Freeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, True))
menu.addAction(_("Unfreeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, False))
coins = self.wallet.get_spendable_coins(addrs)
if coins:
if self.parent.utxo_list.are_in_coincontrol(coins):
menu.addAction(_("Remove from coin control"), lambda: self.parent.utxo_list.remove_from_coincontrol(coins))
if self.main_window.utxo_list.are_in_coincontrol(coins):
menu.addAction(_("Remove from coin control"), lambda: self.main_window.utxo_list.remove_from_coincontrol(coins))
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)
menu.exec_(self.viewport().mapToGlobal(position))
@ -316,7 +321,7 @@ class AddressList(MyTreeView):
try:
self.wallet.check_address_for_corruption(text)
except InternalAddressCorruption as e:
self.parent.show_error(str(e))
self.main_window.show_error(str(e))
raise
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)
def on_edited(self, idx, edit_key, *, text):
self.parent.wallet.set_label(edit_key, text)
self.parent.history_model.refresh('address label edited')
self.parent.utxo_list.update()
self.parent.update_completions()
self.wallet.set_label(edit_key, text)
self.main_window.history_model.refresh('address label edited')
self.main_window.utxo_list.update()
self.main_window.update_completions()

71
electrum/gui/qt/channels_list.py

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import traceback
import enum
from typing import Sequence, Optional, Dict
from typing import Sequence, Optional, Dict, TYPE_CHECKING
from abc import abstractmethod, ABC
from PyQt5 import QtCore, QtGui
@ -24,6 +24,9 @@ from .util import (MyTreeView, WindowModalDialog, Buttons, OkButton, CancelButto
from .amountedit import BTCAmountEdit, FreezableLineEdit
from .util import read_QIcon, font_height
if TYPE_CHECKING:
from .main_window import ElectrumWindow
ROLE_CHANNEL_ID = Qt.UserRole
@ -63,16 +66,18 @@ class ChannelsList(MyTreeView):
_default_item_bg_brush = None # type: Optional[QBrush]
def __init__(self, parent):
super().__init__(parent, self.create_menu, stretch_column=self.Columns.NODE_ALIAS)
def __init__(self, main_window: 'ElectrumWindow'):
super().__init__(
main_window=main_window,
stretch_column=self.Columns.NODE_ALIAS,
)
self.setModel(QtGui.QStandardItemModel(self))
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.main_window = parent
self.gossip_db_loaded.connect(self.on_gossip_db)
self.update_rows.connect(self.do_update_rows)
self.update_single_row.connect(self.do_update_single_row)
self.network = self.parent.network
self.wallet = self.parent.wallet
self.network = self.main_window.network
self.wallet = self.main_window.wallet
self.setSortingEnabled(True)
@property
@ -85,12 +90,12 @@ class ChannelsList(MyTreeView):
for subject in (REMOTE, LOCAL):
if isinstance(chan, Channel):
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()
bal_other = chan.balance(other)//1000
bal_minus_htlcs_other = chan.balance_minus_outgoing_htlcs(other)//1000
if bal_other != bal_minus_htlcs_other:
label += ' (+' + self.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:
assert isinstance(chan, ChannelBackup)
label = ''
@ -98,7 +103,7 @@ class ChannelsList(MyTreeView):
status = chan.get_state_for_GUI()
closed = chan.is_closed()
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 {
self.Columns.SHORT_CHANID: chan.short_id_for_GUI(),
self.Columns.LONG_CHANID: chan.channel_id.hex(),
@ -125,7 +130,7 @@ class ChannelsList(MyTreeView):
self.is_force_close = False
msg = _('Cooperative close?')
msg += '\n' + _(messages.MSG_COOPERATIVE_CLOSE)
if not self.parent.question(msg):
if not self.main_window.question(msg):
return
coro = self.lnworker.close_channel(channel_id)
on_success = self.on_channel_closed
@ -147,10 +152,10 @@ class ChannelsList(MyTreeView):
+ '<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.') + ' '\
+ _('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
if self.save_backup:
if not self.parent.backup_wallet():
if not self.main_window.backup_wallet():
return
def task():
coro = self.lnworker.force_close_channel(channel_id)
@ -178,7 +183,7 @@ class ChannelsList(MyTreeView):
def request_force_close(self, channel_id):
msg = _('Request force-close from remote peer?')
msg += '\n' + _(messages.MSG_REQUEST_FORCE_CLOSE)
if not self.parent.question(msg):
if not self.main_window.question(msg):
return
def task():
coro = self.lnworker.request_force_close(channel_id)
@ -208,9 +213,9 @@ class ChannelsList(MyTreeView):
def on_rebalance(self):
chan1, chan2 = self.get_rebalance_pair()
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
self.parent.rebalance_dialog(chan1, chan2)
self.main_window.rebalance_dialog(chan1, chan2)
def create_menu(self, position):
menu = QMenu()
@ -222,7 +227,7 @@ class ChannelsList(MyTreeView):
if len(selected) == 2:
chan1, chan2 = self.get_rebalance_pair()
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))
return
elif len(selected) > 2:
@ -235,7 +240,7 @@ class ChannelsList(MyTreeView):
return
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]
menu.addAction(_("Details..."), lambda: self.parent.show_channel_details(chan))
menu.addAction(_("Details..."), lambda: self.main_window.show_channel_details(chan))
menu.addSeparator()
cc = self.add_copy_menu(menu, idx)
cc.addAction(_("Node ID"), lambda: self.place_text_on_clipboard(
@ -272,7 +277,7 @@ class ChannelsList(MyTreeView):
@QtCore.pyqtSlot(Abstract_Wallet, AbstractChannel)
def do_update_single_row(self, wallet: Abstract_Wallet, chan: AbstractChannel):
if wallet != self.parent.wallet:
if wallet != self.wallet:
return
for row in range(self.model().rowCount()):
item = self.model().item(row, self.Columns.NODE_ALIAS)
@ -287,11 +292,11 @@ class ChannelsList(MyTreeView):
@QtCore.pyqtSlot()
def on_gossip_db(self):
self.do_update_rows(self.parent.wallet)
self.do_update_rows(self.wallet)
@QtCore.pyqtSlot(Abstract_Wallet)
def do_update_rows(self, wallet):
if wallet != self.parent.wallet:
if wallet != self.wallet:
return
self.model().clear()
self.update_headers(self.headers)
@ -337,29 +342,29 @@ class ChannelsList(MyTreeView):
item.setToolTip("")
def update_can_send(self, lnworker: LNWallet):
msg = _('Can send') + ' ' + self.parent.format_amount(lnworker.num_sats_can_send())\
+ ' ' + self.parent.base_unit() + '; '\
+ _('can receive') + ' ' + self.parent.format_amount(lnworker.num_sats_can_receive())\
+ ' ' + self.parent.base_unit()
msg = _('Can send') + ' ' + self.main_window.format_amount(lnworker.num_sats_can_send())\
+ ' ' + self.main_window.base_unit() + '; '\
+ _('can receive') + ' ' + self.main_window.format_amount(lnworker.num_sats_can_receive())\
+ ' ' + self.main_window.base_unit()
self.can_send_label.setText(msg)
def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu('')
self.can_send_label = toolbar.itemAt(0).widget()
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.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.setEnabled(self.parent.wallet.has_lightning())
self.new_channel_button.setEnabled(self.wallet.has_lightning())
toolbar.insertWidget(2, self.new_channel_button)
return toolbar
def new_channel_with_warning(self):
lnworker = self.parent.wallet.lnworker
lnworker = self.wallet.lnworker
if not lnworker.channels and not lnworker.channel_backups:
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)
if answer:
self.new_channel_dialog()
@ -367,9 +372,9 @@ class ChannelsList(MyTreeView):
self.new_channel_dialog()
def statistics_dialog(self):
channel_db = self.parent.network.channel_db
capacity = self.parent.format_amount(channel_db.capacity()) + ' '+ self.parent.base_unit()
d = WindowModalDialog(self.parent, _('Lightning Network Statistics'))
channel_db = self.network.channel_db
capacity = self.main_window.format_amount(channel_db.capacity()) + ' '+ self.main_window.base_unit()
d = WindowModalDialog(self.main_window, _('Lightning Network Statistics'))
d.setMinimumWidth(400)
vbox = QVBoxLayout(d)
h = QGridLayout()
@ -385,7 +390,7 @@ class ChannelsList(MyTreeView):
def new_channel_dialog(self, *, amount_sat=None, min_amount_sat=None):
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()
def set_visibility_of_columns(self):

30
electrum/gui/qt/contact_list.py

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

68
electrum/gui/qt/history_list.py

@ -486,12 +486,12 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
return True
return False
def __init__(self, parent, model: HistoryModel):
super().__init__(parent, self.create_menu,
def __init__(self, main_window: 'ElectrumWindow', model: HistoryModel):
super().__init__(
main_window=main_window,
stretch_column=HistoryColumns.DESCRIPTION,
editable_columns=[HistoryColumns.DESCRIPTION, HistoryColumns.FIAT_VALUE])
self.main_window = parent
self.config = parent.config
editable_columns=[HistoryColumns.DESCRIPTION, HistoryColumns.FIAT_VALUE],
)
self.hm = model
self.proxy = HistorySortModel(self)
self.proxy.setSourceModel(model)
@ -510,7 +510,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
self.end_button.setEnabled(False)
self.period_combo.addItems([_('All'), _('Custom')])
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.setRootIsDecorated(True)
self.header().setStretchLastSection(False)
@ -603,24 +603,24 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
def show_summary(self):
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
fx = self.parent.fx
fx = self.main_window.fx
h = self.wallet.get_detailed_history(
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,
fx=fx)
summary = h['summary']
if not summary:
self.parent.show_message(_("Nothing to summarize."))
self.main_window.show_message(_("Nothing to summarize."))
return
start = summary['begin']
end = summary['end']
flow = summary['flow']
start_date = start.get('date')
end_date = end.get('date')
format_amount = lambda x: self.parent.format_amount(x.value) + ' ' + self.parent.base_unit()
format_fiat = lambda x: str(x) + ' ' + self.parent.fx.ccy
format_amount = lambda x: self.main_window.format_amount(x.value) + ' ' + self.main_window.base_unit()
format_fiat = lambda x: str(x) + ' ' + self.main_window.fx.ccy
d = WindowModalDialog(self, _("Summary"))
d.setMinimumSize(600, 150)
@ -679,7 +679,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
from electrum.plot import plot_history, NothingToPlotException
except Exception as e:
_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' +
_("Perhaps some dependencies are missing...") + " (matplotlib?)" + '\n' +
f"Error: {e!r}"
@ -689,7 +689,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
plt = plot_history(list(self.hm.transactions.values()))
plt.show()
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):
index = self.model().mapToSource(idx)
@ -699,9 +699,9 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
if column == HistoryColumns.DESCRIPTION:
if self.wallet.set_label(key, text): #changed
self.hm.update_label(index)
self.parent.update_completions()
self.main_window.update_completions()
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
if value is not None:
self.hm.update_fiat(index)
@ -720,13 +720,13 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
else:
if tx_item.get('lightning'):
if tx_item['type'] == 'payment':
self.parent.show_lightning_transaction(tx_item)
self.main_window.show_lightning_transaction(tx_item)
return
tx_hash = tx_item['txid']
tx = self.wallet.adb.get_transaction(tx_hash)
if not tx:
return
self.parent.show_transaction(tx)
self.main_window.show_transaction(tx)
def add_copy_menu(self, menu, idx):
cc = menu.addMenu(_("Copy"))
@ -751,14 +751,14 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
tx_item = idx.internalPointer().get_data()
if tx_item.get('lightning') and tx_item['type'] == 'payment':
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.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"))
key = tx_item['payment_hash']
log = self.wallet.lnworker.logs.get(key)
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))
return
tx_hash = tx_item['txid']
@ -780,25 +780,25 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
# TODO use siblingAtColumn when min Qt version is >=5.11
persistent = QPersistentModelIndex(org_idx.sibling(org_idx.row(), c))
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')
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 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:
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:
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)
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:
menu_invs = menu.addMenu(_("Related 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:
menu.addAction(_("View on block explorer"), lambda: webopen(tx_URL))
menu.exec_(self.viewport().mapToGlobal(position))
@ -809,24 +809,24 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
if num_child_txs > 0:
question = (_("Are you sure you want to remove this transaction and {} child transactions?")
.format(num_child_txs))
if not self.parent.question(msg=question,
if not self.main_window.question(msg=question,
title=_("Please confirm")):
return
self.wallet.adb.remove_transaction(tx_hash)
self.wallet.save_db()
# 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):
try:
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:
self.parent.show_error(e)
self.main_window.show_error(e)
return
if not tx:
return
self.parent.save_transaction_into_wallet(tx)
self.main_window.save_transaction_into_wallet(tx)
def export_history_dialog(self):
d = WindowModalDialog(self, _('Export History'))
@ -850,12 +850,12 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
self.do_export_history(filename, csv_button.isChecked())
except (IOError, os.error) as reason:
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
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):
hist = self.wallet.get_detailed_history(fx=self.parent.fx)
hist = self.wallet.get_detailed_history(fx=self.main_window.fx)
txns = hist['transactions']
lines = []
if is_csv:

14
electrum/gui/qt/invoice_list.py

@ -69,8 +69,10 @@ class InvoiceList(MyTreeView):
def __init__(self, send_tab: 'SendTab'):
window = send_tab.window
super().__init__(window, self.create_menu,
stretch_column=self.Columns.DESCRIPTION)
super().__init__(
main_window=window,
stretch_column=self.Columns.DESCRIPTION,
)
self.wallet = window.wallet
self.send_tab = send_tab
self.std_model = QStandardItemModel(self)
@ -115,7 +117,7 @@ class InvoiceList(MyTreeView):
labels = [""] * len(self.Columns)
labels[self.Columns.DATE] = format_time(timestamp) if timestamp else _('Unknown')
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)
items = [QStandardItem(e) for e in labels]
self.set_editability(items)
@ -160,11 +162,11 @@ class InvoiceList(MyTreeView):
copy_menu = self.add_copy_menu(menu, idx)
address = invoice.get_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():
menu.addAction(_("Details"), lambda: self.parent.show_lightning_invoice(invoice))
menu.addAction(_("Details"), lambda: self.main_window.show_lightning_invoice(invoice))
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)
if status == PR_UNPAID:
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'):
window = receive_tab.window
super().__init__(window, self.create_menu,
stretch_column=self.Columns.DESCRIPTION)
super().__init__(
main_window=window,
stretch_column=self.Columns.DESCRIPTION,
)
self.wallet = window.wallet
self.receive_tab = receive_tab
self.std_model = QStandardItemModel(self)
@ -141,7 +143,7 @@ class RequestList(MyTreeView):
amount = req.get_amount_sat()
message = req.get_message()
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[self.Columns.DATE] = date
labels[self.Columns.DESCRIPTION] = message
@ -193,15 +195,15 @@ class RequestList(MyTreeView):
menu = QMenu(self)
copy_menu = self.add_copy_menu(menu, idx)
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):
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():
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:
# menu.addAction(_("View in web browser"), lambda: webopen(req['view_url']))
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))
def delete_requests(self, keys):

22
electrum/gui/qt/util.py

@ -626,14 +626,21 @@ class MyTreeView(QTreeView):
Columns: Type[BaseColumnsEnum]
def __init__(self, parent: 'ElectrumWindow', create_menu, *,
stretch_column=None, editable_columns=None):
def __init__(
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)
self.parent = parent
self.config = self.parent.config
self.main_window = main_window
self.config = self.main_window.config if self.main_window else None
self.stretch_column = stretch_column
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(create_menu)
self.customContextMenuRequested.connect(self.create_menu)
self.setUniformRowHeights(True)
# Control which columns are editable
@ -659,6 +666,9 @@ class MyTreeView(QTreeView):
self._default_bg_brush = QStandardItem().background()
def create_menu(self, position: QPoint) -> None:
pass
def set_editability(self, items):
for idx, i in enumerate(items):
i.setEditable(idx in self.editable_columns)
@ -836,7 +846,7 @@ class MyTreeView(QTreeView):
return cc
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'):
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
# SOFTWARE.
from typing import Optional, List, Dict, Sequence, Set
from typing import Optional, List, Dict, Sequence, Set, TYPE_CHECKING
import enum
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 .new_channel_dialog import NewChannelDialog
if TYPE_CHECKING:
from .main_window import ElectrumWindow
class UTXOList(MyTreeView):
_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
key_role = ROLE_PREVOUT_STR
def __init__(self, parent):
super().__init__(parent, self.create_menu,
stretch_column=self.stretch_column)
def __init__(self, main_window: 'ElectrumWindow'):
super().__init__(
main_window=main_window,
stretch_column=self.stretch_column,
)
self._spend_set = set()
self._utxo_dict = {}
self.wallet = self.parent.wallet
self.wallet = self.main_window.wallet
self.std_model = QStandardItemModel(self)
self.setModel(self.std_model)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
@ -95,7 +100,7 @@ class UTXOList(MyTreeView):
labels = [""] * len(self.Columns)
labels[self.Columns.OUTPOINT] = str(utxo.short_id)
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]
self.set_editability(utxo_item)
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._filter_frozen_coins(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))
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:
self.parent.set_coincontrol_msg(None)
self.main_window.set_coincontrol_msg(None)
def refresh_row(self, key, row):
assert row is not None
@ -184,7 +189,7 @@ class UTXOList(MyTreeView):
selected = self.get_selected_outpoints()
coins = [self._utxo_dict[name] for name in selected]
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
self.add_to_coincontrol(coins)
@ -223,7 +228,7 @@ class UTXOList(MyTreeView):
def swap_coins(self, coins):
#self.clear_coincontrol()
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()
def can_open_channel(self, coins):
@ -236,7 +241,7 @@ class UTXOList(MyTreeView):
# todo : use a single dialog in new flow
#self.clear_coincontrol()
self.add_to_coincontrol(coins)
d = NewChannelDialog(self.parent)
d = NewChannelDialog(self.main_window)
d.max_button.setChecked(True)
d.max_button.setEnabled(False)
d.min_button.setEnabled(False)
@ -247,15 +252,15 @@ class UTXOList(MyTreeView):
self.clear_coincontrol()
def clipboard_contains_address(self):
text = self.parent.app.clipboard().text()
text = self.main_window.app.clipboard().text()
return is_address(text)
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, '!')]
#self.clear_coincontrol()
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()
def create_menu(self, position):
@ -277,7 +282,7 @@ class UTXOList(MyTreeView):
tx = self.wallet.adb.get_transaction(txid)
if tx:
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
menu_spend = menu.addMenu(_("Fully spend") + '')
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
menu_freeze = menu.addMenu(_("Freeze"))
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:
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):
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:
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
menu.addSeparator()
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]
menu_freeze = menu.addMenu(_("Freeze"))
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):
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):
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):
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))

11
electrum/gui/qt/watchtower_dialog.py

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

Loading…
Cancel
Save