332 lines
11 KiB
Python
332 lines
11 KiB
Python
|
|
import enum
|
|
import copy
|
|
import json
|
|
import urllib.request
|
|
import urllib.parse
|
|
from functools import partial
|
|
|
|
from . import qt_resources
|
|
if qt_resources.QT_VERSION == 5:
|
|
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QPalette, QColor
|
|
from PyQt5.QtCore import Qt,QPersistentModelIndex, QModelIndex
|
|
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,QMenu,QAbstractItemView,QWidget)
|
|
else:
|
|
from PyQt6.QtGui import QStandardItemModel, QStandardItem, QPalette, QColor
|
|
from PyQt6.QtCore import Qt,QPersistentModelIndex, QModelIndex
|
|
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,QMenu,QAbstractItemView,QWidget)
|
|
|
|
from electrum.i18n import _
|
|
from electrum.gui.qt.util import (Buttons,read_QIcon, import_meta_gui, export_meta_gui,MessageBoxMixin)
|
|
from electrum.util import write_json_file,read_json_file,FileImportFailed
|
|
from electrum.gui.qt.my_treeview import MyTreeView
|
|
from electrum.transaction import tx_from_any
|
|
from electrum.network import Network
|
|
|
|
|
|
from ..bal import BalPlugin
|
|
from .. import willexecutors as Willexecutors
|
|
from .. import util as Util
|
|
from .. import will as Will
|
|
from .baldialog import BalDialog
|
|
|
|
class PreviewList(MyTreeView):
|
|
class Columns(MyTreeView.BaseColumnsEnum):
|
|
LOCKTIME = enum.auto()
|
|
TXID = enum.auto()
|
|
WILLEXECUTOR = enum.auto()
|
|
STATUS = enum.auto()
|
|
|
|
headers = {
|
|
Columns.LOCKTIME: _('Locktime'),
|
|
Columns.TXID: _('Txid'),
|
|
Columns.WILLEXECUTOR: _('Will-Executor'),
|
|
Columns.STATUS: _('Status'),
|
|
}
|
|
|
|
ROLE_HEIR_KEY = Qt.ItemDataRole.UserRole + 2000
|
|
key_role = ROLE_HEIR_KEY
|
|
|
|
def __init__(self, parent: 'BalWindow',will):
|
|
super().__init__(
|
|
parent=parent.window,
|
|
stretch_column=self.Columns.TXID,
|
|
)
|
|
self.decimal_point=parent.bal_plugin.config.get_decimal_point
|
|
self.setModel(QStandardItemModel(self))
|
|
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
|
|
|
|
|
if not will is None:
|
|
self.will = will
|
|
else:
|
|
self.will = parent.willitems
|
|
|
|
self.bal_window = parent
|
|
self.wallet=parent.window.wallet
|
|
self.setModel(QStandardItemModel(self))
|
|
self.setSortingEnabled(True)
|
|
self.std_model = self.model()
|
|
self.config = parent.bal_plugin.config
|
|
self.bal_plugin=self.bal_window.bal_plugin
|
|
|
|
self.update()
|
|
|
|
def create_menu(self, position):
|
|
menu = QMenu()
|
|
idx = self.indexAt(position)
|
|
column = idx.column() or self.Columns.TXID
|
|
selected_keys = []
|
|
for s_idx in self.selected_in_column(self.Columns.TXID):
|
|
sel_key = self.model().itemFromIndex(s_idx).data(0)
|
|
selected_keys.append(sel_key)
|
|
if selected_keys and idx.isValid():
|
|
column_title = self.model().horizontalHeaderItem(column).text()
|
|
column_data = '\n'.join(self.model().itemFromIndex(s_idx).text()
|
|
for s_idx in self.selected_in_column(column))
|
|
|
|
menu.addAction(_("details").format(column_title), lambda: self.show_transaction(selected_keys)).setEnabled(len(selected_keys)<2)
|
|
menu.addAction(_("check ").format(column_title), lambda: self.check_transactions(selected_keys))
|
|
|
|
menu.addSeparator()
|
|
menu.addAction(_("delete").format(column_title), lambda: self.delete(selected_keys))
|
|
|
|
menu.exec(self.viewport().mapToGlobal(position))
|
|
|
|
def delete(self,selected_keys):
|
|
for key in selected_keys:
|
|
del self.will[key]
|
|
try:
|
|
del self.bal_window.willitems[key]
|
|
except:
|
|
pass
|
|
try:
|
|
del self.bal_window.will[key]
|
|
except:
|
|
pass
|
|
self.update()
|
|
|
|
def check_transactions(self,selected_keys):
|
|
wout = {}
|
|
for k in selected_keys:
|
|
wout[k] = self.will[k]
|
|
if wout:
|
|
self.bal_window.check_transactions(wout)
|
|
self.update()
|
|
|
|
def show_transaction(self,selected_keys):
|
|
for key in selected_keys:
|
|
self.bal_window.show_transaction(self.will[key].tx)
|
|
|
|
self.update()
|
|
|
|
def select(self,selected_keys):
|
|
self.selected += selected_keys
|
|
self.update()
|
|
|
|
def deselect(self,selected_keys):
|
|
for key in selected_keys:
|
|
self.selected.remove(key)
|
|
self.update()
|
|
|
|
def update_will(self,will):
|
|
self.will.update(will)
|
|
self.update()
|
|
|
|
def update(self):
|
|
if self.will is None:
|
|
return
|
|
|
|
current_key = self.get_role_data_for_current_item(col=self.Columns.TXID, role=self.ROLE_HEIR_KEY)
|
|
self.model().clear()
|
|
self.update_headers(self.__class__.headers)
|
|
|
|
|
|
|
|
|
|
set_current = None
|
|
for txid,bal_tx in self.will.items():
|
|
if self.bal_window.bal_plugin._hide_replaced and bal_tx.get_status('REPLACED'):
|
|
continue
|
|
if self.bal_window.bal_plugin._hide_invalidated and bal_tx.get_status('INVALIDATED'):
|
|
continue
|
|
|
|
|
|
tx=bal_tx.tx
|
|
labels = [""] * len(self.Columns)
|
|
labels[self.Columns.LOCKTIME] = Util.locktime_to_str(tx.locktime)
|
|
labels[self.Columns.TXID] = txid
|
|
we = 'None'
|
|
if bal_tx.we:
|
|
we = bal_tx.we['url']
|
|
labels[self.Columns.WILLEXECUTOR]=we
|
|
status = bal_tx.status
|
|
if len(bal_tx.status) > 53:
|
|
status = "...{}".format(status[-50:])
|
|
labels[self.Columns.STATUS] = status
|
|
|
|
|
|
|
|
items=[]
|
|
for e in labels:
|
|
if type(e)== list:
|
|
try:
|
|
items.append(QStandardItem(*e))
|
|
except Exception as e:
|
|
pass
|
|
else:
|
|
items.append(QStandardItem(str(e)))
|
|
|
|
#pal = QPalette()
|
|
#pal.setColor(QPalette.ColorRole.Window, QColor(bal_tx.get_color()))
|
|
#items[-1].setAutoFillBackground(True)
|
|
#items[-1o].setPalette(pal)
|
|
items[-1].setBackground(QColor(bal_tx.get_color()))
|
|
|
|
|
|
self.model().insertRow(self.model().rowCount(), items)
|
|
if txid == current_key:
|
|
idx = self.model().index(row_count, self.Columns.TXID)
|
|
set_current = QPersistentModelIndex(idx)
|
|
self.set_current_idx(set_current)
|
|
|
|
def create_toolbar(self, config):
|
|
toolbar, menu = self.create_toolbar_with_menu('')
|
|
menu.addAction(_("Prepare"), self.build_transactions)
|
|
menu.addAction(_("Display"), self.bal_window.preview_modal_dialog)
|
|
menu.addAction(_("Sign"), self.ask_password_and_sign_transactions)
|
|
menu.addAction(_("Export"), self.export_will)
|
|
#menu.addAction(_("Import"), self.import_will)
|
|
menu.addAction(_("Broadcast"), self.broadcast)
|
|
menu.addAction(_("Check"), self.check)
|
|
menu.addAction(_("Invalidate"), self.invalidate_will)
|
|
prepareButton = QPushButton(_("Prepare"))
|
|
prepareButton.clicked.connect(self.build_transactions)
|
|
signButton = QPushButton(_("Sign"))
|
|
signButton.clicked.connect(self.ask_password_and_sign_transactions)
|
|
pushButton = QPushButton(_("Broadcast"))
|
|
pushButton.clicked.connect(self.broadcast)
|
|
displayButton = QPushButton(_("Display"))
|
|
displayButton.clicked.connect(self.bal_window.preview_modal_dialog)
|
|
hlayout = QHBoxLayout()
|
|
widget = QWidget()
|
|
hlayout.addWidget(prepareButton)
|
|
hlayout.addWidget(signButton)
|
|
hlayout.addWidget(pushButton)
|
|
hlayout.addWidget(displayButton)
|
|
widget.setLayout(hlayout)
|
|
toolbar.insertWidget(2,widget)
|
|
|
|
return toolbar
|
|
|
|
def hide_replaced(self):
|
|
self.bal_window.bal_plugin.hide_replaced()
|
|
self.update()
|
|
|
|
def hide_invalidated(self):
|
|
f.bal_window.bal_plugin.hide_invalidated()
|
|
self.update()
|
|
|
|
def build_transactions(self):
|
|
will = self.bal_window.prepare_will()
|
|
if will:
|
|
self.update_will(will)
|
|
|
|
def export_json_file(self,path):
|
|
write_json_file(path, self.will)
|
|
|
|
def export_will(self):
|
|
self.bal_window.export_will()
|
|
self.update()
|
|
|
|
def import_will(self):
|
|
self.bal_window.import_will()
|
|
|
|
def ask_password_and_sign_transactions(self):
|
|
self.bal_window.ask_password_and_sign_transactions(callback=self.update)
|
|
|
|
def broadcast(self):
|
|
self.bal_window.broadcast_transactions()
|
|
self.update()
|
|
|
|
def check(self):
|
|
self.bal_window.check_transactions(self.bal_window.willitems)
|
|
self.update()
|
|
|
|
def invalidate_will(self):
|
|
self.bal_window.invalidate_will()
|
|
self.update()
|
|
|
|
class PreviewDialog(BalDialog,MessageBoxMixin):
|
|
def __init__(self, bal_window, will):
|
|
self.parent = bal_window.window
|
|
BalDialog.__init__(self,bal_window = bal_window)
|
|
self.bal_plugin = bal_window.bal_plugin
|
|
self.gui_object = self.bal_plugin.gui_object
|
|
self.config = self.bal_plugin.config
|
|
self.bal_window = bal_window
|
|
self.wallet = bal_window.window.wallet
|
|
self.format_amount = bal_window.window.format_amount
|
|
self.base_unit = bal_window.window.base_unit
|
|
self.format_fiat_and_units = bal_window.window.format_fiat_and_units
|
|
self.fx = bal_window.window.fx
|
|
self.format_fee_rate = bal_window.window.format_fee_rate
|
|
self.show_address = bal_window.window.show_address
|
|
if not will:
|
|
self.will = bal_window.willitems
|
|
else:
|
|
self.will = will
|
|
self.setWindowTitle(_('Transactions Preview'))
|
|
self.setMinimumSize(1000, 200)
|
|
self.size_label = QLabel()
|
|
self.transactions_list = PreviewList(self.bal_window,self.will)
|
|
vbox = QVBoxLayout(self)
|
|
vbox.addWidget(self.size_label)
|
|
vbox.addWidget(self.transactions_list)
|
|
buttonbox = QHBoxLayout()
|
|
|
|
b = QPushButton(_('Sign'))
|
|
b.clicked.connect(self.transactions_list.ask_password_and_sign_transactions)
|
|
buttonbox.addWidget(b)
|
|
|
|
b = QPushButton(_('Export Will'))
|
|
b.clicked.connect(self.transactions_list.export_will)
|
|
buttonbox.addWidget(b)
|
|
|
|
b = QPushButton(_('Broadcast'))
|
|
b.clicked.connect(self.transactions_list.broadcast)
|
|
buttonbox.addWidget(b)
|
|
|
|
b = QPushButton(_('Invalidate will'))
|
|
b.clicked.connect(self.transactions_list.invalidate_will)
|
|
buttonbox.addWidget(b)
|
|
|
|
vbox.addLayout(buttonbox)
|
|
|
|
self.update()
|
|
|
|
|
|
def update_will(self,will):
|
|
self.will.update(will)
|
|
self.transactions_list.update_will(will)
|
|
self.update()
|
|
|
|
def update(self):
|
|
self.transactions_list.update()
|
|
|
|
def is_hidden(self):
|
|
return self.isMinimized() or self.isHidden()
|
|
|
|
def show_or_hide(self):
|
|
if self.is_hidden():
|
|
self.bring_to_top()
|
|
else:
|
|
self.hide()
|
|
|
|
def bring_to_top(self):
|
|
self.show()
|
|
self.raise_()
|
|
|
|
def closeEvent(self, event):
|
|
event.accept()
|