import enum import json import urllib.request import urllib.parse from . import qt_resources if qt_resources.QT_VERSION == 5: from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtCore import Qt,QPersistentModelIndex, QModelIndex from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,QMenu) else: from PyQt6.QtGui import QStandardItemModel, QStandardItem from PyQt6.QtCore import Qt,QPersistentModelIndex, QModelIndex from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,QMenu) 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 from electrum.gui.qt.my_treeview import MyTreeView from ..bal import BalPlugin from .. import util as Util from .. import willexecutors as Willexecutors from .baldialog import BalDialog,BalBlockingWaitingDialog from electrum.logging import get_logger,Logger _logger=get_logger(__name__) class WillExecutorList(MyTreeView): class Columns(MyTreeView.BaseColumnsEnum): SELECTED = enum.auto() URL = enum.auto() BASE_FEE = enum.auto() INFO = enum.auto() ADDRESS = enum.auto() STATUS = enum.auto() headers = { Columns.SELECTED:_(''), Columns.URL: _('Url'), Columns.BASE_FEE: _('Base fee'), Columns.INFO:_('Info'), Columns.ADDRESS:_('Default Address'), Columns.STATUS: _('S'), } ROLE_HEIR_KEY = Qt.ItemDataRole.UserRole + 2000 key_role = ROLE_HEIR_KEY def __init__(self, parent: 'WillExecutorDialog'): super().__init__( parent=parent, stretch_column=self.Columns.ADDRESS, editable_columns=[self.Columns.URL,self.Columns.BASE_FEE,self.Columns.ADDRESS,self.Columns.INFO], ) self.parent = parent self.setModel(QStandardItemModel(self)) self.setSortingEnabled(True) self.std_model = self.model() self.config =parent.bal_plugin.config self.update() def create_menu(self, position): menu = QMenu() idx = self.indexAt(position) column = idx.column() or self.Columns.URL selected_keys = [] for s_idx in self.selected_in_column(self.Columns.URL): 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)) if Willexecutors.is_selected(self.parent.willexecutors_list[sel_key]): menu.addAction(_("deselect").format(column_title), lambda: self.deselect(selected_keys)) else: menu.addAction(_("select").format(column_title), lambda: self.select(selected_keys)) if column in self.editable_columns: item = self.model().itemFromIndex(idx) if item.isEditable(): persistent = QPersistentModelIndex(idx) menu.addAction(_("Edit {}").format(column_title), lambda p=persistent: self.edit(QModelIndex(p))) menu.addAction(_("Ping").format(column_title), lambda: self.ping_willexecutors(selected_keys)) menu.addSeparator() menu.addAction(_("delete").format(column_title), lambda: self.delete(selected_keys)) menu.exec(self.viewport().mapToGlobal(position)) def ping_willexecutors(self,selected_keys): wout={} for k in selected_keys: wout[k]=self.parent.willexecutors_list[k] self.parent.update_willexecutors(wout) self.update() def get_edit_key_from_coordinate(self, row, col): a= self.get_role_data_from_coordinate(row, col, role=self.ROLE_HEIR_KEY+col) return a def delete(self,selected_keys): for key in selected_keys: del self.parent.willexecutors_list[key] self.update() def select(self,selected_keys): for wid,w in self.parent.willexecutors_list.items(): if wid in selected_keys: w['selected']=True self.update() def deselect(self,selected_keys): for wid,w in self.parent.willexecutors_list.items(): if wid in selected_keys: w['selected']=False self.update() def on_edited(self, idx, edit_key, *, text): prior_name = self.parent.willexecutors_list[edit_key] col = idx.column() try: if col == self.Columns.URL: self.parent.willexecutors_list[text]=self.parent.willexecutors_list[edit_key] del self.parent.willexecutors_list[edit_key] if col == self.Columns.BASE_FEE: self.parent.willexecutors_list[edit_key]["base_fee"] = Util.encode_amount(text,self.config.get_decimal_point()) if col == self.Columns.ADDRESS: self.parent.willexecutors_list[edit_key]["address"] = text if col == self.Columns.INFO: self.parent.willexecutors_list[edit_key]["info"] = text self.update() except Exception as e: pass def update(self): if self.parent.willexecutors_list is None: return try: current_key = self.get_role_data_for_current_item(col=self.Columns.URL, role=self.ROLE_HEIR_KEY) self.model().clear() self.update_headers(self.__class__.headers) set_current = None for url, value in self.parent.willexecutors_list.items(): labels = [""] * len(self.Columns) labels[self.Columns.URL] = url if Willexecutors.is_selected(value): labels[self.Columns.SELECTED] = [read_QIcon('confirmed.png'),''] else: labels[self.Columns.SELECTED] = '' labels[self.Columns.BASE_FEE] = Util.decode_amount(value.get('base_fee',0),self.config.get_decimal_point()) if str(value.get('status',0)) == "200": labels[self.Columns.STATUS] = [read_QIcon('status_connected.png'),''] else: labels[self.Columns.STATUS] = [read_QIcon('unconfirmed.png'),''] labels[self.Columns.ADDRESS] = str(value.get('address','')) labels[self.Columns.INFO] = str(value.get('info','')) items=[] for e in labels: if type(e)== list: try: items.append(QStandardItem(*e)) except Exception as e: pass else: items.append(QStandardItem(e)) items[self.Columns.SELECTED].setEditable(False) items[self.Columns.URL].setEditable(True) items[self.Columns.ADDRESS].setEditable(True) items[self.Columns.INFO].setEditable(True) items[self.Columns.BASE_FEE].setEditable(True) items[self.Columns.STATUS].setEditable(False) items[self.Columns.URL].setData(url, self.ROLE_HEIR_KEY+1) items[self.Columns.BASE_FEE].setData(url, self.ROLE_HEIR_KEY+2) items[self.Columns.INFO].setData(url, self.ROLE_HEIR_KEY+3) items[self.Columns.ADDRESS].setData(url, self.ROLE_HEIR_KEY+4) self.model().insertRow(self.model().rowCount(), items) if url == current_key: idx = self.model().index(row_count, self.Columns.NAME) set_current = QPersistentModelIndex(idx) self.set_current_idx(set_current) self.parent.save_willexecutors() except Exception as e: _logger.error(e) class WillExecutorDialog(BalDialog,MessageBoxMixin): def __init__(self, bal_window): BalDialog.__init__(self,bal_window.window) self.bal_plugin = bal_window.bal_plugin self.config = self.bal_plugin.config self.window = bal_window.window self.bal_window = bal_window self.willexecutors_list = Willexecutors.get_willexecutors(self.bal_plugin) self.setWindowTitle(_('Will-Executor Service List')) self.setMinimumSize(1000, 200) self.size_label = QLabel() self.willexecutor_list = WillExecutorList(self) vbox = QVBoxLayout(self) vbox.addWidget(self.size_label) vbox.addWidget(self.willexecutor_list) buttonbox = QHBoxLayout() b = QPushButton(_('Ping')) b.clicked.connect(self.update_willexecutors) buttonbox.addWidget(b) b = QPushButton(_('Import')) b.clicked.connect(self.import_file) buttonbox.addWidget(b) b = QPushButton(_('Export')) b.clicked.connect(self.export_file) buttonbox.addWidget(b) b = QPushButton(_('Add')) b.clicked.connect(self.add) buttonbox.addWidget(b) b = QPushButton(_('Close')) b.clicked.connect(self.close) buttonbox.addWidget(b) vbox.addLayout(buttonbox) self.willexecutor_list.update() def add(self): self.willexecutors_list["http://localhost:8080"]={"info":"New Will Executor","base_fee":0,"status":"-1"} self.willexecutor_list.update() def import_file(self): import_meta_gui(self, _('willexecutors.json'), self.import_json_file, self.willexecutors_list.update) def export_file(self, path): Util.export_meta_gui(self, _('willexecutors.json'), self.export_json_file) def export_json_file(self,path): write_json_file(path, self.willexecutors_list) def update_willexecutors(self,wes=None): if not wes: self.willexecutors_list = Willexecutors.get_willexecutors(self.bal_plugin, update = True, bal_window = self.bal_window,force=True) else: self.bal_window.ping_willexecutors(wes) self.willexecutors_list.update(wes) self.willexecutor_list.update() def import_json_file(self, path): data = read_json_file(path) data = self._validate(data) self.willexecutors_list.update(data) self.willexecutor_list.update() #TODO validate willexecutor json import file def _validate(self,data): return data 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() def save_willexecutors(self): self.bal_plugin.config.set_key(self.bal_plugin.WILLEXECUTORS,self.willexecutors_list,save=True)