def get_empty_text(self) -> str: return _("No new notifications.")
from electrumsv.transaction import Transaction from electrumsv.util import profiler, format_time from electrumsv.wallet import AbstractAccount from electrumsv.wallet_database import TxData import electrumsv.web as web from .constants import ICON_NAME_INVOICE_PAYMENT from .main_window import ElectrumWindow from .util import read_QIcon, get_source_index # NOTE: Filtering does not work as this is QTableView and does not add filter proxy like KeyView(). QT_SORT_ROLE = Qt.UserRole + 1 COLUMN_NAMES = [ _("Date Added"), _("Date Updated"), _("State"), _('Label'), _('Value'), '' ] DATE_ADDED_COLUMN = 0 DATE_UPDATED_COLUMN = 1 STATE_COLUMN = 2 LABEL_COLUMN = 3 VALUE_COLUMN = 4 FIAT_VALUE_COLUMN = 5 class EventFlags(enum.IntFlag):
def _event_create_menu(self, position): menu = QMenu() # What the user clicked on. menu_index = self.indexAt(position) menu_source_index = get_source_index(menu_index, _ItemModel) if menu_source_index.row() != -1: menu_line = self._data[menu_source_index.row()] menu_column = menu_source_index.column() column_title = self._headers[menu_column] if menu_column == 0: copy_text = hash_to_hex_str(menu_line.hash) else: copy_text = str(menu_source_index.model().data( menu_source_index, Qt.DisplayRole)).strip() menu.addAction( _("Copy {}").format(column_title), lambda: self._main_window.app.clipboard().setText(copy_text)) # The row selection. selected_indexes = self.selectedIndexes() if len(selected_indexes): # This is an index on the sort/filter model, translate it to the base model. selected = [] for selected_index in selected_indexes: base_index = get_source_index(selected_index, _ItemModel) row = base_index.row() column = base_index.column() line = self._data[row] selected.append( (row, column, line, selected_index, base_index)) rows = set(v[0] for v in selected) multi_select = len(rows) > 1 if not multi_select: row, column, line, selected_index, base_index = selected[0] menu.addAction( _('Details'), lambda: self._main_window.show_transaction( self._account, self._account.get_transaction(line.hash) )) entry = self._account.get_transaction_entry(line.hash) if entry.flags & TxFlags.PaysInvoice: menu.addAction( self._invoice_icon, _("View invoice"), partial(self._show_invoice_window, line.hash)) line_URL = web.BE_URL(self._main_window.config, 'tx', hash_to_hex_str(line.hash)) if line_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(line_URL)) menu.addSeparator() if column == LABEL_COLUMN: menu.addAction( _("Edit {}").format(column_title), lambda: self.edit(selected_index)) if entry.flags & TxFlags.STATE_UNCLEARED_MASK != 0: if entry.flags & TxFlags.PaysInvoice: broadcast_action = menu.addAction( self._invoice_icon, _("Pay invoice"), lambda: self._pay_invoice(line.hash)) row = self._account.invoices.get_invoice_for_tx_hash( line.hash) if row is None: # The associated invoice has been deleted. broadcast_action.setEnabled(False) elif row.flags & PaymentFlag.UNPAID == 0: # The associated invoice has already been paid. broadcast_action.setEnabled(False) elif has_expired(row.date_expires): # The associated invoice has expired. broadcast_action.setEnabled(False) else: menu.addAction( _("Broadcast"), lambda: self._broadcast_transaction(line.hash)) menu.addSeparator() menu.addAction( _("Remove from account"), partial(self._delete_transaction, line.hash)) menu.exec_(self.viewport().mapToGlobal(position))
def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins)
class PasswordLayout(object): titles = [_("Enter Password"), _("Change Password"), _("Enter Passphrase")] def __init__(self, wallet, msg, kind, OK_button): self.wallet = wallet self.pw = PasswordLineEdit() self.new_pw = PasswordLineEdit() self.conf_pw = PasswordLineEdit() self.kind = kind self.OK_button = OK_button vbox = QVBoxLayout() label = QLabel(msg + "\n") label.setWordWrap(True) grid = QGridLayout() grid.setSpacing(8) grid.setColumnMinimumWidth(0, 150) grid.setColumnMinimumWidth(1, 100) grid.setColumnStretch(1, 1) if kind == PW_PASSPHRASE: vbox.addWidget(label) msgs = [_('Passphrase:'), _('Confirm Passphrase:')] else: logo_grid = QGridLayout() logo_grid.setSpacing(8) logo_grid.setColumnMinimumWidth(0, 70) logo_grid.setColumnStretch(1, 1) logo = QLabel() logo.setAlignment(Qt.AlignCenter) logo_grid.addWidget(logo, 0, 0) logo_grid.addWidget(label, 0, 1, 1, 2) vbox.addLayout(logo_grid) m1 = _('New Password:'******'Password:'******'Confirm Password:'******'Current Password:'******'Encrypt wallet file')) self.encrypt_cb.setEnabled(False) grid.addWidget(self.encrypt_cb, 4, 0, 1, 2) self.encrypt_cb.setVisible(kind != PW_PASSPHRASE) def enable_OK(): ok = self.new_pw.text() == self.conf_pw.text() OK_button.setEnabled(ok) self.encrypt_cb.setEnabled(ok and bool(self.new_pw.text())) self.new_pw.textChanged.connect(enable_OK) self.conf_pw.textChanged.connect(enable_OK) self.vbox = vbox def title(self): return self.titles[self.kind] def layout(self): return self.vbox def pw_changed(self): password = self.new_pw.text() if password: colors = { "Weak": "Red", "Medium": "Blue", "Strong": "Green", "Very Strong": "Green" } strength = check_password_strength(password) label = (_("Password Strength") + ": " + "<font color=" + colors[strength] + ">" + strength + "</font>") else: label = "" self.pw_strength.setText(label) def old_password(self): if self.kind == PW_CHANGE: return self.pw.text() or None return None def new_password(self): pw = self.new_pw.text() # Empty passphrases are fine and returned empty. if pw == "" and self.kind != PW_PASSPHRASE: pw = None return pw
def sign_transaction(self, tx, password): if tx.is_complete(): return client = self.get_client() inputs = [] inputsPaths = [] pubKeys = [] chipInputs = [] redeemScripts = [] signatures = [] preparedTrustedInputs = [] changePath = "" changeAmount = None output = None outputAmount = None p2shTransaction = False pin = "" self.get_client( ) # prompt for the PIN before displaying the dialog if necessary # Fetch inputs of the transaction to sign derivations = self.get_tx_derivations(tx) for txin in tx.inputs(): if txin['type'] == 'coinbase': self.give_error( "Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin) for i, x_pubkey in enumerate(x_pubkeys): if x_pubkey in derivations: signingPos = i s = derivations.get(x_pubkey) hwAddress = "{:s}/{:d}/{:d}".format( self.get_derivation()[2:], s[0], s[1]) break else: self.give_error("No matching x_key for sign_transaction" ) # should never happen redeemScript = Transaction.get_preimage_script(txin) inputs.append([ txin['prev_tx'].raw, txin['prevout_n'], redeemScript, txin['prevout_hash'], signingPos, txin.get('sequence', 0xffffffff - 1) ]) inputsPaths.append(hwAddress) pubKeys.append(pubkeys) # Sanity check if p2shTransaction: for txin in tx.inputs(): if txin['type'] != 'p2sh': # should never happen self.give_error( "P2SH / regular input mixed in same transaction not supported" ) txOutput = var_int(len(tx.outputs())) for txout in tx.outputs(): output_type, addr, amount = txout txOutput += int_to_hex(amount, 8) script = tx.pay_script(addr) txOutput += var_int(len(script) // 2) txOutput += script txOutput = bfh(txOutput) # Recognize outputs - only one output and one change is authorized if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if (info is not None) and (len(tx.outputs()) != 1): index, xpubs, m = info changePath = self.get_derivation( )[2:] + "/{:d}/{:d}".format(*index) changeAmount = amount else: output = address outputAmount = amount self.handler.show_message( _("Confirm Transaction on your Ledger device...")) try: # Get trusted inputs from the original transactions for utxo in inputs: sequence = int_to_hex(utxo[5], 4) txtmp = bitcoinTransaction(bfh(utxo[0])) tmp = bfh(utxo[3])[::-1] tmp += bfh(int_to_hex(utxo[1], 4)) tmp += txtmp.outputs[utxo[1]].amount chipInputs.append({ 'value': tmp, 'witness': True, 'sequence': sequence }) redeemScripts.append(bfh(utxo[2])) # Sign all inputs inputIndex = 0 rawTx = tx.serialize() self.get_client().enableAlternate2fa(False) self.get_client().startUntrustedTransaction( True, inputIndex, chipInputs, redeemScripts[inputIndex]) outputData = self.get_client().finalizeInputFull(txOutput) outputData['outputData'] = txOutput transactionOutput = outputData['outputData'] if outputData['confirmationNeeded']: outputData['address'] = output self.handler.finished() pin = self.handler.get_auth( outputData) # the authenticate dialog and returns pin if not pin: raise UserWarning() if pin != 'paired': self.handler.show_message( _("Confirmed. Signing Transaction...")) while inputIndex < len(inputs): singleInput = [chipInputs[inputIndex]] self.get_client().startUntrustedTransaction( False, 0, singleInput, redeemScripts[inputIndex]) inputSignature = self.get_client().untrustedHashSign( inputsPaths[inputIndex], pin, lockTime=tx.locktime, sighashType=tx.nHashType()) inputSignature[0] = 0x30 # force for 1.4.9+ signatures.append(inputSignature) inputIndex = inputIndex + 1 except UserWarning: self.handler.show_error(_('Cancelled by user')) return except BTChipException as e: if e.sw == 0x6985: # cancelled by user return else: logger.exception("") self.give_error(e, True) except Exception as e: logger.exception("") self.give_error(e, True) finally: self.handler.finished() for i, txin in enumerate(tx.inputs()): signingPos = inputs[i][4] txin['signatures'][signingPos] = bh2u(signatures[i]) tx.raw = tx.serialize()
from electrumsv.keystore import is_xprv, Hardware_KeyStore from electrumsv.i18n import _ from electrumsv.util import bh2u from electrumsv.gui.qt.main_window import ElectrumWindow from electrumsv.gui.qt.util import ( WindowModalDialog, WWLabel, Buttons, CancelButton, OkButton, CloseButton, ) PASSPHRASE_HELP_SHORT = _( "Passphrases allow you to access new wallets, each " "hidden behind a particular case-sensitive passphrase.") PASSPHRASE_HELP = PASSPHRASE_HELP_SHORT + " " + _( "You need to create a separate ElectrumSV wallet for each passphrase " "you use as they each generate different addresses. Changing " "your passphrase does not lose other wallets, each is still " "accessible behind its own passphrase.") RECOMMEND_PIN = _( "You should enable PIN protection. Your PIN is the only protection " "for your bitcoins if your device is lost or stolen.") PASSPHRASE_NOT_PIN = _( "If you forget a passphrase you will be unable to access any " "bitcoins in the wallet behind it. A passphrase is not a PIN. " "Only change this if you are sure you understand it.") CHARACTER_RECOVERY = ( "Use the recovery cipher shown on your device to input your seed words. "
from electrumsv.util import bh2u from electrumsv.gui.qt.util import ( WindowModalDialog, WWLabel, Buttons, CancelButton, OkButton, CloseButton, ) from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from .trezor import (TrezorPlugin, TIM_RECOVER, RECOVERY_TYPE_SCRAMBLED_WORDS, RECOVERY_TYPE_MATRIX) PASSPHRASE_HELP_SHORT = _( "Passphrases allow you to access new wallets, each " "hidden behind a particular case-sensitive passphrase.") PASSPHRASE_HELP = PASSPHRASE_HELP_SHORT + " " + _( "You need to create a separate ElectrumSV wallet for each passphrase " "you use as they each generate different addresses. Changing " "your passphrase does not lose other wallets, each is still " "accessible behind its own passphrase.") RECOMMEND_PIN = _( "You should enable PIN protection. Your PIN is the only protection " "for your bitcoins if your device is lost or stolen.") PASSPHRASE_NOT_PIN = _( "If you forget a passphrase you will be unable to access any " "bitcoins in the wallet behind it. A passphrase is not a PIN. " "Only change this if you are sure you understand it.") MATRIX_RECOVERY = _( "Enter the recovery words by pressing the buttons according to what "
def request_trezor_init_settings(self, wizard, method, model): vbox = QVBoxLayout() next_enabled = True label = QLabel(_("Enter a label to name your device:")) name = QLineEdit() hl = QHBoxLayout() hl.addWidget(label) hl.addWidget(name) hl.addStretch(1) vbox.addLayout(hl) def clean_text(widget): text = widget.toPlainText().strip() return ' '.join(text.split()) gb = QGroupBox() hbox1 = QHBoxLayout() gb.setLayout(hbox1) vbox.addWidget(gb) gb.setTitle(_("Select your seed length:")) bg_numwords = QButtonGroup() for i, count in enumerate([12, 18, 24]): rb = QRadioButton(gb) rb.setText(_("%d words") % count) bg_numwords.addButton(rb) bg_numwords.setId(rb, i) hbox1.addWidget(rb) rb.setChecked(True) cb_pin = QCheckBox(_('Enable PIN protection')) cb_pin.setChecked(True) vbox.addWidget(WWLabel(RECOMMEND_PIN)) vbox.addWidget(cb_pin) passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") cb_phrase = QCheckBox(_('Enable passphrases')) cb_phrase.setChecked(False) vbox.addWidget(passphrase_msg) vbox.addWidget(passphrase_warning) vbox.addWidget(cb_phrase) # ask for recovery type (random word order OR matrix) if method == TIM_RECOVER and not model == 'T': gb_rectype = QGroupBox() hbox_rectype = QHBoxLayout() gb_rectype.setLayout(hbox_rectype) vbox.addWidget(gb_rectype) gb_rectype.setTitle(_("Select recovery type:")) bg_rectype = QButtonGroup() rb1 = QRadioButton(gb_rectype) rb1.setText(_('Scrambled words')) bg_rectype.addButton(rb1) bg_rectype.setId(rb1, RECOVERY_TYPE_SCRAMBLED_WORDS) hbox_rectype.addWidget(rb1) rb1.setChecked(True) rb2 = QRadioButton(gb_rectype) rb2.setText(_('Matrix')) bg_rectype.addButton(rb2) bg_rectype.setId(rb2, RECOVERY_TYPE_MATRIX) hbox_rectype.addWidget(rb2) else: bg_rectype = None wizard.exec_layout(vbox, next_enabled=next_enabled) item = bg_numwords.checkedId() pin = cb_pin.isChecked() recovery_type = bg_rectype.checkedId() if bg_rectype else None return (item, name.text(), pin, cb_phrase.isChecked(), recovery_type)
UNCONFIRMED = 1 UNVERIFIED = 2 UNMATURED = 3 FINAL = 4 TX_ICONS = [ "icons8-question-mark-96.png", # Missing. "icons8-checkmark-grey-52.png", # Unconfirmed. "icons8-checkmark-grey-52.png", # Unverified. "icons8-lock-96.png", # Unmatured. "icons8-checkmark-green-52.png", # Confirmed / verified. ] TX_STATUS = { TxStatus.FINAL: _('Confirmed'), TxStatus.MISSING: _('Missing'), TxStatus.UNCONFIRMED: _('Unconfirmed'), TxStatus.UNMATURED: _('Unmatured'), TxStatus.UNVERIFIED: _('Unverified'), } # This was intended to see if increasing the cell height would cause the monospace fonts to be # aligned in the center. # class ItemDelegate(QItemDelegate): # def __init__(self, parent: Optional[QWidget], height: int=-1) -> None: # super().__init__(parent) # self._height = height # def set_height(self, height: int) -> None: # self._height = height
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 create_menu(self, position): selected = self.get_selected() if not selected: return menu = QMenu() coins = [coin for coin in self.utxos if self.get_name(coin) in selected] spendable_coins = [coin for coin in coins if not selected.get(self.get_name(coin), '')] # Unconditionally add the "Spend" option but leave it disabled if there are no # spendable_coins action = menu.addAction(_("Spend"), lambda: self.parent.spend_coins(spendable_coins)) action.setEnabled(bool(spendable_coins)) if len(selected) == 1: # single selection, offer them the "Details" option and also coin/address # "freeze" status, if any txid = list(selected.keys())[0].split(':')[0] frozen_flags = list(selected.values())[0] tx = self.wallet.transactions.get(txid) menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx)) needsep = True if 'c' in frozen_flags: menu.addSeparator() menu.addAction(_("Coin is frozen"), lambda: None).setEnabled(False) menu.addAction(_("Unfreeze Coin"), lambda: self.set_frozen_coins(list(selected.keys()), False)) menu.addSeparator() needsep = False else: menu.addAction(_("Freeze Coin"), lambda: self.set_frozen_coins(list(selected.keys()), True)) if 'a' in frozen_flags: if needsep: menu.addSeparator() menu.addAction(_("Address is frozen"), lambda: None).setEnabled(False) menu.addAction(_("Unfreeze Address"), lambda: self.set_frozen_addresses_for_coins(list(selected.keys()), False)) else: menu.addAction(_("Freeze Address"), lambda: self.set_frozen_addresses_for_coins(list(selected.keys()), True)) else: # multi-selection menu.addSeparator() if any(['c' not in flags for flags in selected.values()]): # they have some coin-level non-frozen in the selection, so add the menu # action "Freeze coins" menu.addAction(_("Freeze Coins"), lambda: self.set_frozen_coins(list(selected.keys()), True)) if any(['c' in flags for flags in selected.values()]): # they have some coin-level frozen in the selection, so add the menu # action "Unfreeze coins" menu.addAction(_("Unfreeze Coins"), lambda: self.set_frozen_coins(list(selected.keys()), False)) if any(['a' not in flags for flags in selected.values()]): # they have some address-level non-frozen in the selection, so add the # menu action "Freeze addresses" menu.addAction(_("Freeze Addresses"), lambda: self.set_frozen_addresses_for_coins(list(selected.keys()), True)) if any(['a' in flags for flags in selected.values()]): # they have some address-level frozen in the selection, so add the menu # action "Unfreeze addresses" menu.addAction(_("Unfreeze Addresses"), lambda: self.set_frozen_addresses_for_coins(list(selected.keys()), False)) menu.exec_(self.viewport().mapToGlobal(position))
def get_entry_image_text(self, row: WalletEventRow) -> str: image_text = _("Warning") return image_text
def get_entry_text(self, row: WalletEventRow) -> str: if row.event_type == WalletEventType.SEED_BACKUP_REMINDER: return _("Add text about how users should backup their seeds.") # Intentionally not localised. return "Not yet implemented"
def decrypt_message(self, sequence, message, password): raise RuntimeError(_('Encryption and decryption are not implemented by {}').format( self.device))
def create_form_layout(self) -> QHBoxLayout: # A 4-column grid layout. All the stretch is in the last column. # The exchange rate plugin adds a fiat widget in column 2 grid = QGridLayout() grid.setSpacing(8) grid.setColumnStretch(3, 1) self._receive_destination_e = ButtonsLineEdit() self._receive_destination_e.addCopyButton(app_state.app) self._receive_destination_e.setReadOnly(True) msg = _( 'Bitcoin SV payment destination where the payment should be received. ' 'Note that each payment request uses a different Bitcoin SV payment destination.' ) receive_address_label = HelpLabel(_('Receiving destination'), msg) self._receive_destination_e.textChanged.connect( self._update_receive_qr) self._receive_destination_e.setFocusPolicy(Qt.NoFocus) grid.addWidget(receive_address_label, 0, 0) grid.addWidget(self._receive_destination_e, 0, 1, 1, -1) self._receive_message_e = QLineEdit() grid.addWidget(QLabel(_('Description')), 1, 0) grid.addWidget(self._receive_message_e, 1, 1, 1, -1) self._receive_message_e.textChanged.connect(self._update_receive_qr) self._receive_amount_e = BTCAmountEdit() grid.addWidget(QLabel(_('Requested amount')), 2, 0) grid.addWidget(self._receive_amount_e, 2, 1) self._receive_amount_e.textChanged.connect(self._update_receive_qr) self._fiat_receive_e = AmountEdit( app_state.fx.get_currency if app_state.fx else '') if not app_state.fx or not app_state.fx.is_enabled(): self._fiat_receive_e.setVisible(False) grid.addWidget(self._fiat_receive_e, 2, 2, Qt.AlignLeft) self._main_window.connect_fields(self._receive_amount_e, self._fiat_receive_e) self._expires_combo = QComboBox() self._expires_combo.addItems([i[0] for i in expiration_values]) self._expires_combo.setCurrentIndex(3) self._expires_combo.setFixedWidth(self._receive_amount_e.width()) msg = ' '.join([ _('Expiration date of your request.'), _('This information is seen by the recipient if you send them ' 'a signed payment request.'), _('Expired requests have to be deleted manually from your list, ' 'in order to free the corresponding Bitcoin SV addresses.'), _('The Bitcoin SV address never expires and will always be part ' 'of this ElectrumSV wallet.'), ]) grid.addWidget(HelpLabel(_('Request expires'), msg), 3, 0) grid.addWidget(self._expires_combo, 3, 1) self._expires_label = QLineEdit('') self._expires_label.setReadOnly(1) self._expires_label.setFocusPolicy(Qt.NoFocus) self._expires_label.hide() grid.addWidget(self._expires_label, 3, 1) self._save_request_button = EnterButton(_('Save request'), self._save_form_as_request) self._new_request_button = EnterButton(_('New'), self._new_payment_request) self._receive_qr = QRCodeWidget(fixedSize=200) self._receive_qr.link_to_window(self._toggle_qr_window) buttons = QHBoxLayout() buttons.addStretch(1) buttons.addWidget(self._save_request_button) buttons.addWidget(self._new_request_button) grid.addLayout(buttons, 4, 1, 1, 2) vbox_g = QVBoxLayout() vbox_g.addLayout(grid) vbox_g.addStretch() hbox = QHBoxLayout() hbox.addLayout(vbox_g) hbox.addWidget(self._receive_qr) return hbox
def decrypt_message(self, pubkey, message, password): raise RuntimeError( _('Encryption and decryption are not supported for {}').format( self.device))
from electrumsv.app_state import app_state from electrumsv.constants import AccountType, CHANGE_SUBPATH, RECEIVING_SUBPATH, ScriptType from electrumsv.exceptions import NotEnoughFunds from electrumsv.i18n import _ from electrumsv.logs import logs from electrumsv.networks import Net from electrumsv.transaction import Transaction, XTxOutput from electrumsv.wallet import AbstractAccount from .main_window import ElectrumWindow from .util import EnterButton, HelpDialogButton logger = logs.get_logger("coinsplitting") TX_DESC_PREFIX = _("ElectrumSV coin splitting") RESULT_DUST_TIMEOUT = -2 RESULT_DIALOG_CLOSED = -1 RESULT_READY_FOR_SPLIT = 0 STAGE_INACTIVE = -1 STAGE_PREPARING = 0 STAGE_OBTAINING_DUST = 1 STAGE_SPLITTING = 2 STAGE_NAMES = { STAGE_INACTIVE: _("Inactive") +".", STAGE_PREPARING: _("Preparing") +"..", STAGE_OBTAINING_DUST: _("Obtaining dust") +"..", STAGE_SPLITTING: _("Splitting coins") +"..",
def __init__(self, main_window: ElectrumWindow, parent: QWidget, keystore: KeyStore, password: str) -> None: super().__init__(parent) self._main_window = main_window self.setWindowTitle(_("Secured Account Data")) self.setMinimumSize(500, 200) vbox = QVBoxLayout() self._form = form = FormSectionWidget(minimum_label_width=120) assert keystore.derivation_type in (DerivationType.BIP32, DerivationType.ELECTRUM_OLD) self._seed_edit = None if keystore.seed is not None: seed_text = keystore.get_seed(password) seed_type_text = _("Unknown") if keystore.derivation_type == DerivationType.BIP32: if is_new_seed(seed_text): seed_type_text = _("Electrum") is_checksum_valid, is_wordlist_valid = bip39_is_checksum_valid(seed_text) if is_checksum_valid and is_wordlist_valid: seed_type_text = _("BIP39") elif keystore.derivation_type == DerivationType.ELECTRUM_OLD: seed_type_text = _("Old-style Electrum") form.add_row(_("Seed type"), QLabel(seed_type_text)) seed_edit = ShowQRTextEdit(self) seed_edit.setFixedHeight(80) seed_edit.addCopyButton(self._main_window.app) seed_edit.setText(seed_text) form.add_row(_("Seed phrase"), seed_edit, True) self._seed_edit = seed_edit # Ambiguous if empty string or None. passphrase_widget: QWidget if keystore.passphrase: passphrase_text = keystore.get_passphrase(password) passphrase_edit = ShowQRTextEdit(self) passphrase_edit.setFixedHeight(80) passphrase_edit.addCopyButton(self._main_window.app) passphrase_edit.setText(seed_text) passphrase_widget = passphrase_edit else: passphrase_widget = QLabel(_("None")) form.add_row(_("Passphrase"), passphrase_widget, True) if keystore.derivation_type == DerivationType.BIP32: if keystore.xprv is not None: xprv_text = keystore.get_master_private_key(password) private_key = bip32_key_from_string(xprv_text) xprv_edit = ShowQRTextEdit(self) xprv_edit.setFixedHeight(80) xprv_edit.addCopyButton(self._main_window.app) xprv_edit.setText(private_key.to_extended_key_string()) form.add_row(_("Master private key"), xprv_edit, True) vbox.addWidget(form) vbox.addStretch(1) vbox.addLayout(Buttons(CloseButton(self))) self.setLayout(vbox)
def _on_split_abort(self): self._main_window.show_error(_("Coin-splitting process has been cancelled.")) self._cleanup_tx_final() self._cleanup_tx_created()
def __init__(self, window, plugin, keystore, device_id): title = _("{} Settings").format(plugin.device) super(SettingsDialog, self).__init__(window, title) config = app_state.config hs_rows, hs_cols = (64, 128) def invoke_client(method, *args, **kw_args): unpair_after = kw_args.pop('unpair_after', False) def task(): client = app_state.device_manager.client_by_id(device_id) if not client: raise RuntimeError("Device not connected") if method: getattr(client, method)(*args, **kw_args) if unpair_after: app_state.device_manager.unpair_id(device_id) return client.features window.run_in_thread(task, on_success=update) def update(features): self.features = features set_label_enabled() bl_hash = bh2u(features.bootloader_hash) bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]]) noyes = [_("No"), _("Yes")] endis = [_("Enable Passphrases"), _("Disable Passphrases")] disen = [_("Disabled"), _("Enabled")] setchange = [_("Set a PIN"), _("Change PIN")] version = "%d.%d.%d" % (features.major_version, features.minor_version, features.patch_version) device_label.setText(features.label) pin_set_label.setText(noyes[features.pin_protection]) passphrases_label.setText(disen[features.passphrase_protection]) bl_hash_label.setText(bl_hash) label_edit.setText(features.label) device_id_label.setText(features.device_id) initialized_label.setText(noyes[features.initialized]) version_label.setText(version) clear_pin_button.setVisible(features.pin_protection) clear_pin_warning.setVisible(features.pin_protection) pin_button.setText(setchange[features.pin_protection]) pin_msg.setVisible(not features.pin_protection) passphrase_button.setText(endis[features.passphrase_protection]) language_label.setText(features.language) def set_label_enabled(): label_apply.setEnabled(label_edit.text() != self.features.label) def rename(): invoke_client('change_label', label_edit.text()) def toggle_passphrase(): title = _("Confirm Toggle Passphrase Protection") currently_enabled = self.features.passphrase_protection if currently_enabled: msg = _("After disabling passphrases, you can only pair this " "ElectrumSV wallet if it had an empty passphrase. " "If its passphrase was not empty, you will need to " "create a new wallet with the install wizard. You " "can use this wallet again at any time by re-enabling " "passphrases and entering its passphrase.") else: msg = _("Your current ElectrumSV wallet can only be used with " "an empty passphrase. You must create a separate " "wallet with the install wizard for other passphrases " "as each one generates a new set of addresses.") msg += "\n\n" + _("Are you sure you want to proceed?") if not self.question(msg, title=title): return invoke_client('toggle_passphrase', unpair_after=currently_enabled) def set_pin(): invoke_client('set_pin', remove=False) def clear_pin(): invoke_client('set_pin', remove=True) def wipe_device(): accounts = window._wallet.get_accounts_for_keystore(keystore) if sum(sum(account.get_balance()) for account in accounts): title = _("Confirm Device Wipe") msg = _("Are you SURE you want to wipe the device?\n" "Your wallet still has bitcoins in it!") if not self.question( msg, title=title, icon=QMessageBox.Critical): return invoke_client('wipe_device', unpair_after=True) def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins) def slider_released(): config.set_session_timeout(timeout_slider.sliderPosition() * 60) # Information tab info_tab = QWidget() info_layout = QVBoxLayout(info_tab) info_glayout = QGridLayout() info_glayout.setColumnStretch(2, 1) device_label = QLabel() pin_set_label = QLabel() passphrases_label = QLabel() version_label = QLabel() device_id_label = QLabel() bl_hash_label = QLabel() bl_hash_label.setWordWrap(True) language_label = QLabel() initialized_label = QLabel() rows = [ (_("Device Label"), device_label), (_("PIN set"), pin_set_label), (_("Passphrases"), passphrases_label), (_("Firmware Version"), version_label), (_("Device ID"), device_id_label), (_("Bootloader Hash"), bl_hash_label), (_("Language"), language_label), (_("Initialized"), initialized_label), ] for row_num, (label, widget) in enumerate(rows): info_glayout.addWidget(QLabel(label), row_num, 0) info_glayout.addWidget(widget, row_num, 1) info_layout.addLayout(info_glayout) # Settings tab settings_tab = QWidget() settings_layout = QVBoxLayout(settings_tab) settings_glayout = QGridLayout() # Settings tab - Label label_msg = QLabel( _("Name this {}. If you have multiple devices " "their labels help distinguish them.").format(plugin.device)) label_msg.setWordWrap(True) label_label = QLabel(_("Device Label")) label_edit = QLineEdit() label_edit.setMinimumWidth(150) label_edit.setMaxLength(plugin.MAX_LABEL_LEN) label_apply = QPushButton(_("Apply")) label_apply.clicked.connect(rename) label_edit.textChanged.connect(set_label_enabled) settings_glayout.addWidget(label_label, 0, 0) settings_glayout.addWidget(label_edit, 0, 1, 1, 2) settings_glayout.addWidget(label_apply, 0, 3) settings_glayout.addWidget(label_msg, 1, 1, 1, -1) # Settings tab - PIN pin_label = QLabel(_("PIN Protection")) pin_button = QPushButton() pin_button.clicked.connect(set_pin) settings_glayout.addWidget(pin_label, 2, 0) settings_glayout.addWidget(pin_button, 2, 1) pin_msg = QLabel( _("PIN protection is strongly recommended. " "A PIN is your only protection against someone " "stealing your bitcoins if they obtain physical " "access to your {}.").format(plugin.device)) pin_msg.setWordWrap(True) pin_msg.setStyleSheet("color: red") settings_glayout.addWidget(pin_msg, 3, 1, 1, -1) # Settings tab - Session Timeout timeout_label = QLabel(_("Session Timeout")) timeout_minutes = QLabel() timeout_slider = QSlider(Qt.Horizontal) timeout_slider.setRange(1, 60) timeout_slider.setSingleStep(1) timeout_slider.setTickInterval(5) timeout_slider.setTickPosition(QSlider.TicksBelow) timeout_slider.setTracking(True) timeout_msg = QLabel( _("Clear the session after the specified period " "of inactivity. Once a session has timed out, " "your PIN and passphrase (if enabled) must be " "re-entered to use the device.")) timeout_msg.setWordWrap(True) timeout_slider.setSliderPosition(config.get_session_timeout() // 60) slider_moved() timeout_slider.valueChanged.connect(slider_moved) timeout_slider.sliderReleased.connect(slider_released) settings_glayout.addWidget(timeout_label, 6, 0) settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3) settings_glayout.addWidget(timeout_minutes, 6, 4) settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1) settings_layout.addLayout(settings_glayout) settings_layout.addStretch(1) # Advanced tab advanced_tab = QWidget() advanced_layout = QVBoxLayout(advanced_tab) advanced_glayout = QGridLayout() # Advanced tab - clear PIN clear_pin_button = QPushButton(_("Disable PIN")) clear_pin_button.clicked.connect(clear_pin) clear_pin_warning = QLabel( _("If you disable your PIN, anyone with physical access to your " "{} device can spend your bitcoins.").format(plugin.device)) clear_pin_warning.setWordWrap(True) clear_pin_warning.setStyleSheet("color: red") advanced_glayout.addWidget(clear_pin_button, 0, 2) advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5) # Advanced tab - toggle passphrase protection passphrase_button = QPushButton() passphrase_button.clicked.connect(toggle_passphrase) passphrase_msg = WWLabel(PASSPHRASE_HELP) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") advanced_glayout.addWidget(passphrase_button, 3, 2) advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5) advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5) # Advanced tab - wipe device wipe_device_button = QPushButton(_("Wipe Device")) wipe_device_button.clicked.connect(wipe_device) wipe_device_msg = QLabel( _("Wipe the device, removing all data from it. The firmware " "is left unchanged.")) wipe_device_msg.setWordWrap(True) wipe_device_warning = QLabel( _("Only wipe a device if you have the recovery seed written down " "and the device wallet(s) are empty, otherwise the bitcoins " "will be lost forever.")) wipe_device_warning.setWordWrap(True) wipe_device_warning.setStyleSheet("color: red") advanced_glayout.addWidget(wipe_device_button, 6, 2) advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5) advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5) advanced_layout.addLayout(advanced_glayout) advanced_layout.addStretch(1) tabs = QTabWidget(self) tabs.addTab(info_tab, _("Information")) tabs.addTab(settings_tab, _("Settings")) tabs.addTab(advanced_tab, _("Advanced")) dialog_vbox = QVBoxLayout(self) dialog_vbox.addWidget(tabs) dialog_vbox.addLayout(Buttons(CloseButton(self))) # Update information invoke_client(None)
def update_layout(self) -> None: if self._account is None: vbox = self._create_disabled_layout(_("No active account.")) self._replace_layout(vbox) return intro_text = _("If this account contains coins that may be linked on both the Bitcoin SV " "blockchain and the Bitcoin Cash blockchain, then the approaches listed below " "can be used to unlink (also known as coin-splitting) them. If no approaches are " "enabled or you want to take control of the process, refer to the help offered " "below.") direct_text = _("The recommended approach. This approach " "will combine the coins in this account into a Bitcoin SV only transaction and send " "them back to this account.") if not self._direct_splitting_enabled: direct_text += "<br/><br/>" direct_text += "<i>"+ _("Incompatible with this account type.") +"</i>" faucet_text = _("The fallback approach. This approach requests a very small amount " "of known Bitcoin SV coins and combines it with the coins in this account and sends " "them back to this account.") if not self._faucet_splitting_enabled: faucet_text += "<br/><br/>" faucet_text += "<i>"+ _("Incompatible with this account type.") +"</i>" self._intro_label = QLabel() self._intro_label.setWordWrap(True) self._intro_label.setMaximumWidth(600) self._intro_label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self._intro_label.setText(intro_text) self._intro_label.setMinimumHeight(self._intro_label.sizeHint().height() + 8) self._direct_label = QLabel(direct_text) self._direct_label.setMaximumWidth(300) self._direct_label.setMinimumWidth(300) self._direct_label.setWordWrap(True) self._faucet_label = QLabel(faucet_text) self._faucet_label.setMaximumWidth(300) self._faucet_label.setMinimumWidth(300) self._faucet_label.setWordWrap(True) self._faucet_button = EnterButton(_("Faucet Split"), self._on_faucet_split) self._faucet_button.setEnabled(self._faucet_splitting_enabled) self._direct_button = EnterButton(_("Direct Split"), self._on_direct_split) self._direct_button.setEnabled(self._direct_splitting_enabled) vbox = QVBoxLayout() vbox.addStretch(1) vbox.addWidget(self._intro_label, 0, Qt.AlignCenter) vbox.addStretch(1) grid = QGridLayout() grid.setColumnStretch(0, 1) grid.setColumnMinimumWidth(1, 100) grid.setColumnStretch(3, 1) row_index = 0 line = QFrame() line.setStyleSheet("QFrame { border: 1px solid #E3E2E2; }") line.setFrameShape(QFrame.HLine) line.setFixedHeight(1) grid.addWidget(line, row_index, 1, 1, 2) row_index += 1 grid.addWidget(self._direct_button, row_index, 1, 1, 1, Qt.AlignLeft) grid.addWidget(self._direct_label, row_index, 2, 1, 1, Qt.AlignCenter) row_index += 1 line = QFrame() line.setStyleSheet("QFrame { border: 1px solid #E3E2E2; }") line.setFrameShape(QFrame.HLine) line.setFixedHeight(1) grid.addWidget(line, row_index, 1, 1, 2) row_index += 1 grid.addWidget(self._faucet_button, row_index, 1, 1, 1, Qt.AlignLeft) grid.addWidget(self._faucet_label, row_index, 2, 1, 1, Qt.AlignCenter) row_index += 1 line = QFrame() line.setStyleSheet("QFrame { border: 1px solid #E3E2E2; }") line.setFrameShape(QFrame.HLine) line.setFixedHeight(1) grid.addWidget(line, row_index, 1, 1, 2) row_index += 1 self._help_button = HelpDialogButton(self, "misc", "coinsplitting-tab", _("Help")) vbox.addLayout(grid) vbox.addStretch(1) vbox.addWidget(self._help_button, 0, Qt.AlignCenter) vbox.addStretch(1) self._replace_layout(vbox)
def request_trezor_init_settings(self, wizard, method, device): vbox = QVBoxLayout() next_enabled = True label = QLabel(_("Enter a label to name your device:")) name = QLineEdit() hl = QHBoxLayout() hl.addWidget(label) hl.addWidget(name) hl.addStretch(1) vbox.addLayout(hl) def clean_text(widget): text = widget.toPlainText().strip() return ' '.join(text.split()) if method in [TIM_NEW, TIM_RECOVER]: gb = QGroupBox() hbox1 = QHBoxLayout() gb.setLayout(hbox1) # KeepKey recovery doesn't need a word count if method == TIM_NEW: vbox.addWidget(gb) gb.setTitle(_("Select your seed length:")) bg = QButtonGroup() for i, count in enumerate([12, 18, 24]): rb = QRadioButton(gb) rb.setText(_("{} words").format(count)) bg.addButton(rb) bg.setId(rb, i) hbox1.addWidget(rb) rb.setChecked(True) cb_pin = QCheckBox(_('Enable PIN protection')) cb_pin.setChecked(True) else: text = QTextEdit() text.setMaximumHeight(60) if method == TIM_MNEMONIC: msg = _("Enter your BIP39 mnemonic:") else: def set_enabled(): wizard.next_button.setEnabled(is_xprv(clean_text(text))) msg = _("Enter the master private key beginning with xprv:") text.textChanged.connect(set_enabled) next_enabled = False vbox.addWidget(QLabel(msg)) vbox.addWidget(text) pin = QLineEdit() pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,10}'))) pin.setMaximumWidth(100) hbox_pin = QHBoxLayout() hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):"))) hbox_pin.addWidget(pin) hbox_pin.addStretch(1) if method in [TIM_NEW, TIM_RECOVER]: vbox.addWidget(WWLabel(RECOMMEND_PIN)) vbox.addWidget(cb_pin) else: vbox.addLayout(hbox_pin) passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") cb_phrase = QCheckBox(_('Enable passphrases')) cb_phrase.setChecked(False) vbox.addWidget(passphrase_msg) vbox.addWidget(passphrase_warning) vbox.addWidget(cb_phrase) wizard.exec_layout(vbox, next_enabled=next_enabled) if method in [TIM_NEW, TIM_RECOVER]: item = bg.checkedId() pin = cb_pin.isChecked() else: item = ' '.join(str(clean_text(text)).split()) pin = str(pin.text()) return (item, name.text(), pin, cb_phrase.isChecked())
def __init__(self, network, config, wizard=False): self.network = network self.config = config self.protocol = None self.tor_proxy = None self.filling_in = False self.tabs = tabs = QTabWidget() tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) server_tab = QWidget() proxy_tab = QWidget() blockchain_tab = QWidget() tabs.addTab(blockchain_tab, _('Overview')) tabs.addTab(server_tab, _('Server')) tabs.addTab(proxy_tab, _('Proxy')) if wizard: tabs.setCurrentIndex(1) # server tab grid = QGridLayout(server_tab) grid.setSpacing(8) self.server_host = QLineEdit() self.server_host.setFixedWidth(200) self.server_port = QLineEdit() self.server_port.setFixedWidth(60) self.autoconnect_cb = QCheckBox(_('Select server automatically')) self.autoconnect_cb.setEnabled( self.config.is_modifiable('auto_connect')) self.server_host.editingFinished.connect(self.set_server) self.server_port.editingFinished.connect(self.set_server) self.autoconnect_cb.clicked.connect(self._on_autoconnect_toggled) self.autoconnect_cb.clicked.connect(self.update) msg = ' '.join([ _("If auto-connect is enabled, ElectrumSV will always use a server that " "is on the longest blockchain."), _("If it is disabled, you have to choose a server you want to use. " "ElectrumSV will warn you if your server is lagging.") ]) grid.addWidget(self.autoconnect_cb, 0, 0, 1, 3) grid.addWidget(HelpButton(msg), 0, 4) grid.addWidget(QLabel(_('Server') + ':'), 1, 0) grid.addWidget(self.server_host, 1, 1, 1, 2) grid.addWidget(self.server_port, 1, 3) label = _('Server peers') if network.is_connected() else _( 'Default Servers') grid.addWidget(QLabel(label), 2, 0, 1, 5) self.servers_list = ServerListWidget(self) grid.addWidget(self.servers_list, 3, 0, 1, 5) # Proxy tab grid = QGridLayout(proxy_tab) grid.setSpacing(8) # proxy setting self.proxy_cb = QCheckBox(_('Use proxy')) self.proxy_cb.clicked.connect(self.check_disable_proxy) self.proxy_cb.clicked.connect(self.set_proxy) self.proxy_mode = QComboBox() self.proxy_mode.addItems(list(SVProxy.kinds)) self.proxy_host = QLineEdit() self.proxy_host.setFixedWidth(200) self.proxy_port = QLineEdit() self.proxy_port.setFixedWidth(100) self.proxy_username = QLineEdit() self.proxy_username.setPlaceholderText(_("Proxy user")) self.proxy_username.setFixedWidth(self.proxy_host.width()) self.proxy_password = PasswordLineEdit() self.proxy_password.setPlaceholderText(_("Password")) self.proxy_mode.currentIndexChanged.connect(self.set_proxy) self.proxy_host.editingFinished.connect(self.set_proxy) self.proxy_port.editingFinished.connect(self.set_proxy) self.proxy_username.editingFinished.connect(self.set_proxy) self.proxy_password.editingFinished.connect(self.set_proxy) self.proxy_mode.currentIndexChanged.connect( self.proxy_settings_changed) self.proxy_host.textEdited.connect(self.proxy_settings_changed) self.proxy_port.textEdited.connect(self.proxy_settings_changed) self.proxy_username.textEdited.connect(self.proxy_settings_changed) self.proxy_password.textEdited.connect(self.proxy_settings_changed) self.tor_cb = QCheckBox(_("Use Tor Proxy")) self.tor_cb.setIcon(read_QIcon("tor_logo.png")) self.tor_cb.hide() self.tor_cb.clicked.connect(self.use_tor_proxy) grid.addWidget(self.tor_cb, 1, 0, 1, 3) grid.addWidget(self.proxy_cb, 2, 0, 1, 3) grid.addWidget( HelpButton( _('Proxy settings apply to all connections: both ' 'ElectrumSV servers and third-party services.')), 2, 4) grid.addWidget(self.proxy_mode, 4, 1) grid.addWidget(self.proxy_host, 4, 2) grid.addWidget(self.proxy_port, 4, 3) grid.addWidget(self.proxy_username, 5, 2, Qt.AlignTop) grid.addWidget(self.proxy_password, 5, 3, Qt.AlignTop) grid.setRowStretch(7, 1) # Blockchain Tab grid = QGridLayout(blockchain_tab) msg = ' '.join([ _("ElectrumSV connects to several servers in order to download block headers " "and find out the longest blockchain."), _("This blockchain is used to verify the transactions sent by your " "transaction server.") ]) self.status_label = QLabel('') grid.addWidget(QLabel(_('Status') + ':'), 0, 0) grid.addWidget(self.status_label, 0, 1, 1, 3) grid.addWidget(HelpButton(msg), 0, 4) self.server_label = QLabel('') msg = _( "ElectrumSV sends your wallet addresses to a single server, in order to " "receive your transaction history.") grid.addWidget(QLabel(_('Server') + ':'), 1, 0) grid.addWidget(self.server_label, 1, 1, 1, 3) grid.addWidget(HelpButton(msg), 1, 4) self.height_label = QLabel('') msg = _('This is the height of your local copy of the blockchain.') grid.addWidget(QLabel(_('Blockchain') + ':'), 2, 0) grid.addWidget(self.height_label, 2, 1) grid.addWidget(HelpButton(msg), 2, 4) self.split_label = QLabel('') grid.addWidget(self.split_label, 3, 0, 1, 3) self.nodes_list_widget = NodesListWidget(self) grid.addWidget(self.nodes_list_widget, 5, 0, 1, 5) vbox = QVBoxLayout() vbox.addWidget(tabs) vbox.setSizeConstraint(QVBoxLayout.SetFixedSize) self.layout_ = vbox # tor detector self.td = td = TorDetector() td.found_proxy.connect(self.suggest_proxy) td.start() self.last_values = None self.fill_in_proxy_settings() self.update()
def __init__(self, wallet, msg, kind, OK_button): self.wallet = wallet self.pw = PasswordLineEdit() self.new_pw = PasswordLineEdit() self.conf_pw = PasswordLineEdit() self.kind = kind self.OK_button = OK_button vbox = QVBoxLayout() label = QLabel(msg + "\n") label.setWordWrap(True) grid = QGridLayout() grid.setSpacing(8) grid.setColumnMinimumWidth(0, 150) grid.setColumnMinimumWidth(1, 100) grid.setColumnStretch(1, 1) if kind == PW_PASSPHRASE: vbox.addWidget(label) msgs = [_('Passphrase:'), _('Confirm Passphrase:')] else: logo_grid = QGridLayout() logo_grid.setSpacing(8) logo_grid.setColumnMinimumWidth(0, 70) logo_grid.setColumnStretch(1, 1) logo = QLabel() logo.setAlignment(Qt.AlignCenter) logo_grid.addWidget(logo, 0, 0) logo_grid.addWidget(label, 0, 1, 1, 2) vbox.addLayout(logo_grid) m1 = _('New Password:'******'Password:'******'Confirm Password:'******'Current Password:'******'Encrypt wallet file')) self.encrypt_cb.setEnabled(False) grid.addWidget(self.encrypt_cb, 4, 0, 1, 2) self.encrypt_cb.setVisible(kind != PW_PASSPHRASE) def enable_OK(): ok = self.new_pw.text() == self.conf_pw.text() OK_button.setEnabled(ok) self.encrypt_cb.setEnabled(ok and bool(self.new_pw.text())) self.new_pw.textChanged.connect(enable_OK) self.conf_pw.textChanged.connect(enable_OK) self.vbox = vbox
def __init__(self, parent): QTreeWidget.__init__(self) self.parent = parent self.setHeaderLabels([_('Connected node'), _('Height')]) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.create_menu)
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)
# SOFTWARE. from collections import namedtuple from functools import partial import random from PyQt5.QtWidgets import QGridLayout, QPushButton, QWidget from electrumsv.i18n import _ from electrumsv.app_state import app_state from .util import read_QIcon VKBPage = namedtuple("VKBPage", "tooltip icon chars") pages = [ VKBPage(_('Lower-case letters'), 'text_lowercase.png', 'abcdefghijklmnopqrstuvwxyz_ '), VKBPage(_('Upper-case letters'), 'text_uppercase.png', 'ABCDEFGHIJKLMNOPQRTSUVWXYZ_ '), VKBPage(_('Numbers and symbols'), 'text_symbols.png', '1234567890!?.,;:/%&()[]{}+-=$#*@"\'\\<>~`'), ] max_chars = max(len(page.chars) for page in pages) def vkb_button(click_cb): button = QPushButton() button.clicked.connect(partial(click_cb, button)) button.setFixedWidth(app_state.app.dpi / 3.6) return button
from electrumsv.app_state import app_state from electrumsv.i18n import _ from electrumsv.paymentrequest import PR_UNPAID, PR_PAID, PR_EXPIRED from electrumsv.util import resource_path dialogs = [] pr_icons = { PR_UNPAID: "unpaid.png", PR_PAID: "confirmed.png", PR_EXPIRED: "expired.png" } pr_tooltips = { PR_UNPAID:_('Pending'), PR_PAID:_('Paid'), PR_EXPIRED:_('Expired') } expiration_values = [ (_('1 hour'), 60*60), (_('1 day'), 24*60*60), (_('1 week'), 7*24*60*60), (_('Never'), None) ] class EnterButton(QPushButton): def __init__(self, text, func): QPushButton.__init__(self, text)
def __init__(self, msg: str, fields: Optional[LayoutFields], kind: PasswordAction, state_change_fn: Callable[[bool], None], password_valid_fn: PasswordCheckCallbackType) -> None: self.pw = PasswordLineEdit() self.new_pw = PasswordLineEdit() self.conf_pw = PasswordLineEdit() self.kind = kind self._state_change_fn = state_change_fn # Ensure that callers know what they are doing. The password valid callback only gets used # when users are actually entering it. assert password_valid_fn is None or kind != PasswordAction.CHANGE vbox = QVBoxLayout() label = QLabel(msg + "\n") label.setWordWrap(True) grid = QGridLayout() grid.setSpacing(8) grid.setColumnMinimumWidth(0, 150) grid.setColumnMinimumWidth(1, 100) grid.setColumnStretch(1,1) row = 1 if kind == PasswordAction.PASSPHRASE: vbox.addWidget(label) msgs = [_('Passphrase:'), _('Confirm Passphrase:')] else: logo_grid = QGridLayout() logo_grid.setSpacing(8) logo_grid.setColumnMinimumWidth(0, 70) logo_grid.setColumnStretch(1,1) logo = QLabel() logo.setAlignment(Qt.AlignCenter) logo_grid.addWidget(logo, 0, 0) logo_grid.addWidget(label, 0, 1, 1, 2) vbox.addLayout(logo_grid) if kind == PasswordAction.CHANGE: pwlabel = QLabel(_('Current Password') +":") pwlabel.setAlignment(Qt.AlignTop) grid.addWidget(pwlabel, row, 0, Qt.AlignRight | Qt.AlignVCenter) grid.addWidget(self.pw, row, 1, Qt.AlignLeft) row += 1 m1 = _('New Password') +":" if kind == PasswordAction.CHANGE else _('Password') +":" msgs = [m1, _('Confirm Password') +":"] lockfile = "lock.png" logo.setPixmap(QPixmap(icon_path(lockfile)).scaledToWidth(36)) if fields is not None: for field_label, field_widget in fields: field_label.setAlignment(Qt.AlignTop) grid.addWidget(field_label, row, 0, Qt.AlignRight | Qt.AlignVCenter) grid.addWidget(field_widget, row, 1, Qt.AlignLeft) row += 1 label0 = QLabel(msgs[0]) label0.setAlignment(Qt.AlignTop) grid.addWidget(label0, row, 0, Qt.AlignRight | Qt.AlignVCenter) grid.addWidget(self.new_pw, row, 1, Qt.AlignLeft) row += 1 label1 = QLabel(msgs[1]) label1.setAlignment(Qt.AlignTop) grid.addWidget(label1, row, 0, Qt.AlignRight | Qt.AlignVCenter) grid.addWidget(self.conf_pw, row, 1, Qt.AlignLeft) row += 1 vbox.addLayout(grid) # Password Strength Label if kind != PasswordAction.PASSPHRASE: self._pw_strength_label = QLabel() self._pw_strength = QLabel() grid.addWidget(self._pw_strength_label, row, 0, 1, 1, Qt.AlignRight | Qt.AlignVCenter) grid.addWidget(self._pw_strength, row, 1, 1, 1, Qt.AlignLeft) row += 1 self.new_pw.textChanged.connect(self.pw_changed) def enable_OK() -> None: new_password = self.new_pw.text().strip() confirm_password = self.conf_pw.text().strip() ok = len(new_password) and new_password == confirm_password if password_valid_fn is not None: existing_password = self.pw.text().strip() ok = ok and password_valid_fn(existing_password) self._state_change_fn(ok) self.new_pw.textChanged.connect(enable_OK) self.conf_pw.textChanged.connect(enable_OK) if password_valid_fn is not None: self.pw.textChanged.connect(enable_OK) self.vbox = vbox