def on_update(self) -> None: if self._send_view._account_id is None: return self._stop_timer() current_id = None if self._send_view._payment_request is not None: current_id = self._send_view._payment_request.get_id() if current_id is None: current_item = self.currentItem() current_id = current_item.data( COL_RECEIVED, Qt.UserRole) if current_item else None self.clear() current_item = None current_time = time.time() nearest_expiry_time = float("inf") for row in self._send_view._account.invoices.get_invoices(): flags = row.flags & PaymentFlag.STATE_MASK if flags & PaymentFlag.UNPAID and row.date_expires: if row.date_expires <= current_time + 5: flags = (row.flags & ~PaymentFlag.UNPAID) | PaymentFlag.EXPIRED else: nearest_expiry_time = min(nearest_expiry_time, row.date_expires) requestor_uri = urllib.parse.urlparse(row.payment_uri) requestor_text = requestor_uri.netloc received_text = format_time(row.date_created, _("Unknown")) expires_text = format_time( row.date_expires, _("Unknown") if row.date_expires else _('Never')) item = QTreeWidgetItem([ received_text, expires_text, requestor_text, row.description, app_state.format_amount(row.value, whitespaces=True), # The tooltip text should be None to ensure the icon does not have extra RHS space. pr_tooltips.get(flags, None) ]) icon_entry = pr_icons.get(flags) if icon_entry: item.setIcon(COL_STATUS, read_QIcon(icon_entry)) if row.invoice_id == current_id: current_item = item item.setData(COL_RECEIVED, Qt.UserRole, row.invoice_id) item.setFont(COL_DESCRIPTION, self._monospace_font) item.setFont(COL_AMOUNT, self._monospace_font) self.addTopLevelItem(item) if current_item is not None: self.setCurrentItem(current_item) if nearest_expiry_time != float("inf"): self._start_timer(nearest_expiry_time)
def on_update(self) -> None: if self._account_id is None: return invoices = self._account.invoices inv_list = invoices.unpaid_invoices() self.clear() for pr in inv_list: request_id = pr.get_id() status = invoices.get_status(request_id) requestor = pr.get_requestor() exp = pr.get_expiration_date() date_str = format_time(exp, _("Unknown")) if exp else _('Never') item = QTreeWidgetItem([ date_str, requestor, pr.memo, self._main_window.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status, '') ]) item.setIcon(4, read_QIcon(pr_icons.get(status))) item.setData(0, Qt.UserRole, (self._account_id, request_id)) item.setFont(1, self.monospace_font) item.setFont(3, self.monospace_font) self.addTopLevelItem(item) self.setCurrentItem(self.topLevelItem(0)) self.setVisible(len(inv_list)) self._main_window.invoices_label.setVisible(len(inv_list))
def test_get_tx_desc(self) -> None: from electrumsv.gui.qt.history_list import TxStatus, TX_STATUS, get_tx_desc # Values with a text description should return that text description. for status_kind in [ TxStatus.UNCONFIRMED, TxStatus.MISSING ]: self.assertEqual(TX_STATUS[status_kind], get_tx_desc(status_kind, 1)) # Otherwise the timestamp should be used. time_string = format_time(1, "...") self.assertNotEqual("...", time_string) self.assertEqual(time_string, get_tx_desc(TxStatus.FINAL, 1)) self.assertEqual(_("unknown"), get_tx_desc(TxStatus.FINAL, False))
def on_update(self): self.wallet = self.parent.wallet # hide receive tab if no receive requests available b = len(self.wallet.receive_requests) > 0 self.setVisible(b) self.parent.receive_requests_label.setVisible(b) if not b: self.parent.expires_label.hide() self.parent.expires_combo.show() # update the receive address if necessary current_address_string = self.parent.receive_address_e.text().strip() current_address = (Address.from_string(current_address_string) if len(current_address_string) else None) domain = self.wallet.get_receiving_addresses() addr = self.wallet.get_unused_address() if not current_address in domain and addr: self.parent.set_receive_address(addr) self.parent.new_request_button.setEnabled(addr != current_address) # clear the list and fill it again self.clear() for req in self.wallet.get_sorted_requests(self.config): address = req['address'] if address not in domain: continue timestamp = req.get('time', 0) amount = req.get('amount') expiration = req.get('exp', None) message = req.get('memo', '') date = format_time(timestamp) status = req.get('status') signature = req.get('sig') requestor = req.get('name', '') amount_str = self.parent.format_amount(amount) if amount else "" item = QTreeWidgetItem([ date, address.to_ui_string(), '', message, amount_str, pr_tooltips.get(status, '') ]) item.setData(0, Qt.UserRole, address) if signature is not None: item.setIcon(2, QIcon(":icons/seal.png")) item.setToolTip(2, 'signed by ' + requestor) if status is not PR_UNKNOWN: item.setIcon(6, QIcon(pr_icons.get(status))) self.addTopLevelItem(item)
def on_update(self) -> None: if self._account_id is None: return # hide receive tab if no receive requests available b = len(self._account._payment_requests) > 0 self.setVisible(b) self._main_window.receive_requests_label.setVisible(b) if not b: self._main_window.expires_label.hide() self._main_window.expires_combo.show() # update the receive address if necessary current_key_id = self._main_window.get_receive_key_id() if current_key_id is None: return keyinstance = None if self._account.is_deterministic(): keyinstance = self._account.get_fresh_keys(RECEIVING_SUBPATH, 1)[0] if keyinstance is not None: self._main_window.set_receive_key(keyinstance) self._main_window.new_request_button.setEnabled( current_key_id != keyinstance.keyinstance_id) account_id = self._account.get_id() # clear the list and fill it again self.clear() for req in self._account.get_sorted_requests(): date = format_time(req.date_created, _("Unknown")) amount_str = self._main_window.format_amount( req.value) if req.value else "" script_template = self._account.get_script_template_for_id( req.keyinstance_id) address_text = script_template_to_string(script_template) item = QTreeWidgetItem([ date, address_text, '', req.description or "", amount_str, pr_tooltips.get(req.state, '') ]) item.setData(0, Qt.UserRole, req.paymentrequest_id) if req.state != PaymentState.UNKNOWN: item.setIcon(6, read_QIcon(pr_icons.get(req.state))) self.addTopLevelItem(item)
def get_tx_status(self, tx_hash, height, conf, timestamp): if conf == 0: tx = self.wallet.get_transaction(tx_hash) if not tx: return 3, _('unknown') if height < 0: status = 0 elif height == 0: status = 1 else: status = 2 else: status = 3 + min(conf, 6) if status < len(TX_STATUS): status_str = TX_STATUS[status] else: status_str = format_time(timestamp, _("unknown")) if timestamp else _("unknown") return status, status_str
def add_main_content(self, parent_layout: QHBoxLayout) -> None: layout = QVBoxLayout() parent_layout.addLayout(layout) layout.addStretch(1) if self._row.event_type == WalletEventType.SEED_BACKUP_REMINDER: title_label = QLabel(_("Backup your wallet")) title_label.setObjectName("NotificationCardTitle") description_label = QLabel( _("You should make sure you back up your wallet. If you " "lose access to it, you may not have any way to access or recover your funds and " "any other information it may contain. In the worst case, you may be able to " "write down and use your " "<a href=\"action:view-secured-data\">account's secured data</a>. More information " "is <a href=\"help:view-secured-data\">available here</a>.")) description_label.setObjectName("NotificationCardDescription") description_label.setWordWrap(True) description_label.setTextInteractionFlags( Qt.TextBrowserInteraction) description_label.linkActivated.connect(self.on_link_activated) date_context_label = QLabel( format_time(self._row.date_created, _("Unknown"))) date_context_label.setAlignment(Qt.AlignRight) date_context_label.setObjectName("NotificationCardContext") account_name = self._context.wallet_api.get_account_name( self._row.account_id) account_context_label = QLabel( _("Account: {}").format(account_name)) account_context_label.setObjectName("NotificationCardContext") bottom_layout = QHBoxLayout() bottom_layout.addWidget(account_context_label, 1, Qt.AlignLeft) bottom_layout.addWidget(date_context_label, 1, Qt.AlignRight) layout.addWidget(title_label) layout.addWidget(description_label) layout.addLayout(bottom_layout) else: layout.addWidget(QLabel("Not yet implemented")) layout.addStretch(1)
def on_update(self) -> None: if self._account_id is None: return wallet = self._account._wallet with wallet.get_payment_request_table() as table: rows = table.read(self._account_id, flags=PaymentFlag.NONE, mask=PaymentFlag.ARCHIVED) # update the receive address if necessary current_key_id = self._receive_view.get_receive_key_id() if current_key_id is None: return keyinstance = None if self._account.is_deterministic(): keyinstance = self._account.get_fresh_keys(RECEIVING_SUBPATH, 1)[0] if keyinstance is not None: self._receive_view.set_receive_key(keyinstance) self._receive_view.set_new_button_enabled(current_key_id != keyinstance.keyinstance_id) # clear the list and fill it again self.clear() for row in rows: date = format_time(row.date_created, _("Unknown")) amount_str = app_state.format_amount(row.value, whitespaces=True) if row.value else "" script_template = self._account.get_script_template_for_id(row.keyinstance_id) address_text = script_template_to_string(script_template) state = row.state & sum(pr_icons.keys()) item = QTreeWidgetItem([date, address_text, '', row.description or "", amount_str, pr_tooltips.get(state,'')]) item.setData(0, Qt.UserRole, row.paymentrequest_id) if state != PaymentFlag.UNKNOWN: icon_name = pr_icons.get(state) if icon_name is not None: item.setIcon(6, read_QIcon(icon_name)) item.setFont(4, self._monospace_font) self.addTopLevelItem(item)
def on_update(self): inv_list = self.parent.invoices.unpaid_invoices() self.clear() for pr in inv_list: key = pr.get_id() status = self.parent.invoices.get_status(key) requestor = pr.get_requestor() exp = pr.get_expiration_date() date_str = format_time(exp) if exp else _('Never') item = QTreeWidgetItem([ date_str, requestor, pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status, '') ]) item.setIcon(4, QIcon(pr_icons.get(status))) item.setData(0, Qt.UserRole, key) item.setFont(1, QFont(MONOSPACE_FONT)) item.setFont(3, QFont(MONOSPACE_FONT)) self.addTopLevelItem(item) self.setCurrentItem(self.topLevelItem(0)) self.setVisible(len(inv_list)) self.parent.invoices_label.setVisible(len(inv_list))
def get_tx_desc(status: TxStatus, timestamp: Union[bool, int]) -> str: if status in [TxStatus.UNCONFIRMED, TxStatus.MISSING]: return TX_STATUS[status] return format_time(timestamp, _("unknown")) if timestamp else _("unknown")
def data(self, model_index: QModelIndex, role: int) -> QVariant: row = model_index.row() column = model_index.column() if row >= len(self._data): return None if column >= len(self._column_names): return None if model_index.isValid(): line = self._data[row] # First check the custom sort role. if role == QT_SORT_ROLE: if column == DATE_ADDED_COLUMN: return line.date_added elif column == DATE_UPDATED_COLUMN: return line.date_updated elif column == STATE_COLUMN: if line.flags & TxFlags.StateDispatched: return 0 elif line.flags & TxFlags.StateReceived: return 2 elif line.flags & TxFlags.StateSigned: return 1 else: return 3 elif column == LABEL_COLUMN: return self._view._wallet.get_transaction_label(line.hash) elif column in (VALUE_COLUMN, FIAT_VALUE_COLUMN): return line.value elif role == Qt.DecorationRole: if column == LABEL_COLUMN and line.flags & TxFlags.PaysInvoice: return self._view._invoice_icon elif role == Qt.DisplayRole: if column == DATE_ADDED_COLUMN: return (format_time(line.date_added, _("unknown")) if line.date_added else _("unknown")) elif column == DATE_UPDATED_COLUMN: return (format_time(line.date_updated, _("unknown")) if line.date_updated else _("unknown")) elif column == STATE_COLUMN: if line.flags & TxFlags.StateDispatched: return _("Dispatched") elif line.flags & TxFlags.StateReceived: return _("Received") elif line.flags & TxFlags.StateSigned: return _("Signed") return line.flags elif column == LABEL_COLUMN: return self._view._wallet.get_transaction_label(line.hash) elif column == VALUE_COLUMN: return app_state.format_amount(line.value, whitespaces=True) elif column == FIAT_VALUE_COLUMN: fx = app_state.fx rate = fx.exchange_rate() return fx.value_str(line.value, rate) elif role == Qt.FontRole: if column in (VALUE_COLUMN, FIAT_VALUE_COLUMN): return self._monospace_font elif role == Qt.TextAlignmentRole: if column in (VALUE_COLUMN, FIAT_VALUE_COLUMN): return Qt.AlignRight | Qt.AlignVCenter return Qt.AlignVCenter elif role == Qt.ToolTipRole: if column == LABEL_COLUMN: if line.flags & TxFlags.PaysInvoice: return _("This transaction is associated with an invoice.") elif column == STATE_COLUMN: if line.flags & TxFlags.StateDispatched: return _("This transaction has been sent to the network, but has not " "cleared yet.") elif line.flags & TxFlags.StateReceived: return _("This transaction has been received from another party, but " "has not been broadcast yet.") elif line.flags & TxFlags.StateSigned: return _("This transaction has been signed, but has not been broadcast " "yet.") elif role == Qt.EditRole: if column == LABEL_COLUMN: return self._view._wallet.get_transaction_label(line.hash)
def _format_tx_status(self, status: int, timestamp: int) -> str: if status < len(TX_STATUS): return TX_STATUS[status] return format_time(timestamp, _("Unknown")) if timestamp else _("Unknown")
def __init__(self, main_window: 'ElectrumWindow', row: InvoiceRow) -> None: super().__init__(main_window, _("Invoice")) self.setMinimumWidth(400) self._main_window = weakref.proxy(main_window) self._pr = pr = PaymentRequest.from_json(row.invoice_data) state = row.flags & PaymentFlag.STATE_MASK if state & PaymentFlag.UNPAID and has_expired(row.date_expires): state = PaymentFlag.EXPIRED total_amount = 0 for output in pr.outputs: total_amount += output.amount vbox = QVBoxLayout(self) form = FormSectionWidget(minimum_label_width=120) form.add_row(_('Type'), QLabel(_("BIP270"))) form.add_row(_("State"), QLabel(pr_tooltips.get(state, _("Unknown")))) form.add_row( _('Amount'), QLabel( app_state.format_amount(output.amount) + " " + app_state.base_unit())) form.add_row(_('Memo'), QLabel(row.description)) form.add_row(_('Date Created'), QLabel(format_time(pr.creation_timestamp, _("Unknown")))) form.add_row(_('Date Received'), QLabel(format_time(row.date_created, _("Unknown")))) if row.date_expires: form.add_row(_('Date Expires'), QLabel(format_time(row.date_expires, _("Unknown")))) vbox.addWidget(form) self._table = table = ButtonsTableWidget() table.setSelectionBehavior(QAbstractItemView.SelectRows) table.setSelectionMode(QAbstractItemView.SingleSelection) vh = table.verticalHeader() vh.setSectionResizeMode(QHeaderView.ResizeToContents) vh.hide() table.setColumnCount(3) table.setContextMenuPolicy(Qt.CustomContextMenu) table.customContextMenuRequested.connect(self._on_table_menu) table.setHorizontalHeaderLabels(self._table_column_names) table.setRowCount(len(pr.outputs)) # table.addButton("icons8-copy-to-clipboard-32.png", f, # _("Copy all listed destinations to the clipboard")) # table.addButton("icons8-save-as-32-windows.png", f, # _("Save the listed destinations to a file")) hh = table.horizontalHeader() hh.setStretchLastSection(True) for row, output in enumerate(pr.outputs): label = QLabel( app_state.format_amount(output.amount) + " " + app_state.base_unit()) table.setCellWidget(row, 0, label) table.setCellWidget(row, 1, QLabel(output.description)) kind = classify_output_script(output.script, Net.COIN) text = script_to_display_text(output.script, kind) table.setCellWidget(row, 2, QLabel(text)) vbox.addWidget(table, 1) def do_export(): fn = self._main_window.getSaveFileName(_("Export invoice to file"), "*.bip270.json") if not fn: return with open(fn, 'w') as f: data = f.write(row.invoice_data) self._main_window.show_message(_('Invoice saved as' + ' ' + fn)) exportButton = EnterButton(_('Export'), do_export) def do_delete(): if self.question( _('Are you sure you want to delete this invoice?'), title=_("Delete invoice"), icon=QMessageBox.Warning): self._main_window._send_view._invoice_list._delete_invoice( row.invoice_id) self.close() deleteButton = EnterButton(_('Delete'), do_delete) vbox.addLayout(Buttons(exportButton, deleteButton, CloseButton(self)))