Exemplo n.º 1
0
 def qr_input(self):
     from electrum_dash import qrscanner, get_config
     try:
         data = qrscanner.scan_qr(get_config())
     except BaseException, e:
         QMessageBox.warning(self, _('Error'), _(e), _('OK'))
         return ""
Exemplo n.º 2
0
    def __init__(self, parent):
        super(CharacterDialog, self).__init__(parent)
        self.setWindowTitle(_("KeepKey Seed Recovery"))
        self.character_pos = 0
        self.word_pos = 0
        self.loop = QEventLoop()
        self.word_help = QLabel()
        self.char_buttons = []

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(CHARACTER_RECOVERY))
        hbox = QHBoxLayout()
        hbox.addWidget(self.word_help)
        for i in range(4):
            char_button = CharacterButton('*')
            char_button.setMaximumWidth(36)
            self.char_buttons.append(char_button)
            hbox.addWidget(char_button)
        self.accept_button = CharacterButton(_("Accept Word"))
        self.accept_button.clicked.connect(partial(self.process_key, 32))
        self.rejected.connect(partial(self.loop.exit, 1))
        hbox.addWidget(self.accept_button)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        self.finished_button = QPushButton(_("Seed Entered"))
        self.cancel_button = QPushButton(_("Cancel"))
        self.finished_button.clicked.connect(partial(self.process_key,
                                                     Qt.Key_Return))
        self.cancel_button.clicked.connect(self.rejected)
        buttons = Buttons(self.finished_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Exemplo n.º 3
0
 def on_vote_successful(result):
     errmsg, res = result
     if res:
         QMessageBox.information(self, _('Success'), _('Successfully voted'))
     else:
         QMessageBox.critical(self, _('Error Voting'), _(errmsg))
     self.proposals_widget.editor.vote_button.setEnabled(True)
Exemplo n.º 4
0
 def build_tray_menu(self):
     m = QMenu()
     m.addAction(_("Show/Hide"), self.show_or_hide)
     m.addAction(_("Dark/Light"), self.toggle_tray_icon)
     m.addSeparator()
     m.addAction(_("Exit Electrum-DASH"), self.close)
     self.tray.setContextMenu(m)
Exemplo n.º 5
0
    def __init__(self, transaction_id, parent):
        super(TransactionWindow, self).__init__()

        self.tx_id = str(transaction_id)
        self.parent = parent

        self.setModal(True)
        self.resize(200, 100)
        self.setWindowTitle(_("Transaction successfully sent"))

        self.layout = QGridLayout(self)
        history_label = "%s\n%s" % (
            _("Your transaction has been sent."),
            _("Please enter a label for this transaction for future reference."),
        )
        self.layout.addWidget(QLabel(history_label))

        self.label_edit = QLineEdit()
        self.label_edit.setPlaceholderText(_("Transaction label"))
        self.label_edit.setObjectName("label_input")
        self.label_edit.setAttribute(Qt.WA_MacShowFocusRect, 0)
        self.label_edit.setFocusPolicy(Qt.ClickFocus)
        self.layout.addWidget(self.label_edit)

        self.save_button = QPushButton(_("Save"))
        self.layout.addWidget(self.save_button)
        self.save_button.clicked.connect(self.set_label)

        self.exec_()
Exemplo n.º 6
0
    def cast_vote(self, proposal_name, vote_yes):
        """Vote for a proposal. This is called by ProposalsWidget."""
        vote_choice = 'yes' if vote_yes else 'no'
        mn = self.selected_masternode()
        if not mn.announced:
            return QMessageBox.critical(self, _('Cannot Vote'), _('Masternode has not been activated.'))
        # Check that we can vote before asking for a password.
        try:
            self.manager.check_can_vote(mn.alias, proposal_name)
        except Exception as e:
            return QMessageBox.critical(self, _('Cannot Vote'), _(str(e)))

        self.proposals_widget.editor.vote_button.setEnabled(False)

        def vote_thread():
            return self.manager.vote(mn.alias, proposal_name, vote_choice)

        # Show the result.
        def on_vote_successful(result):
            errmsg, res = result
            if res:
                QMessageBox.information(self, _('Success'), _('Successfully voted'))
            else:
                QMessageBox.critical(self, _('Error Voting'), _(errmsg))
            self.proposals_widget.editor.vote_button.setEnabled(True)

        def on_vote_failed(err):
            self.print_error('Error sending vote:')
            # Print traceback information to error log.
            self.print_error(''.join(traceback.format_tb(err[2])))
            self.print_error(''.join(traceback.format_exception_only(err[0], err[1])))
            self.proposals_widget.editor.vote_button.setEnabled(True)

        util.WaitingDialog(self, _('Voting...'), vote_thread, on_vote_successful, on_vote_failed)
Exemplo n.º 7
0
 def delete_current_masternode(self):
     """Delete the masternode that is being viewed."""
     mn = self.selected_masternode()
     if QMessageBox.question(self, _('Delete'), _('Do you want to remove the masternode configuration for') + ' %s?'%mn.alias,
                 QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes:
         self.masternodes_widget.remove_masternode(mn.alias)
         self.masternodes_widget.view.selectRow(0)
Exemplo n.º 8
0
 def seed_options(self):
     dialog = QDialog()
     vbox = QVBoxLayout(dialog)
     if 'ext' in self.options:
         cb_ext = QCheckBox(_('Extend this seed with custom words'))
         cb_ext.setChecked(self.is_ext)
         vbox.addWidget(cb_ext)
     if 'bip39' in self.options:
         def f(b):
             self.is_seed = (lambda x: bool(x)) if b else self.saved_is_seed
             self.is_bip39 = b
             self.on_edit()
             if b:
                 msg = ' '.join([
                     '<b>' + _('Warning') + ':</b>  ',
                     _('BIP39 seeds can be imported in Dash-Electrum, so that users can access funds locked in other wallets.'),
                     _('However, we do not generate BIP39 seeds, because they do not meet our safety standard.'),
                     _('BIP39 seeds do not include a version number, which compromises compatibility with future software.'),
                     _('We do not guarantee that BIP39 imports will always be supported in Dash-Electrum.'),
                 ])
             else:
                 msg = ''
             self.seed_warning.setText(msg)
         cb_bip39 = QCheckBox(_('BIP39 seed'))
         cb_bip39.toggled.connect(f)
         cb_bip39.setChecked(self.is_bip39)
         vbox.addWidget(cb_bip39)
     vbox.addLayout(Buttons(OkButton(dialog)))
     if not dialog.exec_():
         return None
     self.is_ext = cb_ext.isChecked() if 'ext' in self.options else False
     self.is_bip39 = cb_bip39.isChecked() if 'bip39' in self.options else False
Exemplo n.º 9
0
 def __init__(self, parent):
     MyTreeWidget.__init__(self, parent, self.create_menu, [_('Date'), _('Address'), '', _('Description'), _('Amount'), _('Status')], 3)
     self.currentItemChanged.connect(self.item_changed)
     self.itemClicked.connect(self.item_changed)
     self.setSortingEnabled(True)
     self.setColumnWidth(0, 180)
     self.hideColumn(1)
Exemplo n.º 10
0
    def make_cypherseed(self, img, rawnoise, calibration=False, is_seed = True):
        img = img.convertToFormat(QImage.Format_Mono)
        p = QPainter()
        p.begin(img)
        p.setCompositionMode(26) #xor
        p.drawImage(0, 0, rawnoise)
        p.end()
        cypherseed = self.pixelcode_2x2(img)
        cypherseed = QBitmap.fromImage(cypherseed)
        cypherseed = cypherseed.scaled(self.f_size, Qt.KeepAspectRatio)
        cypherseed = self.overlay_marks(cypherseed, True, calibration)

        if not is_seed:
            self.filename = _('custom_secret')+'_'
            self.was = _('Custom secret')
        else:
            self.filename = self.wallet_name+'_'+ _('seed')+'_'
            self.was = self.wallet_name +' ' + _('seed')

        if self.has_extension:
            self.ext_warning(self.c_dialog)

        if not calibration:
            self.toPdf(QImage(cypherseed))
            QDesktopServices.openUrl (QUrl.fromLocalFile(os.path.abspath(self.base_dir+self.filename+self.version+'_'+self.code_id+'.pdf')))
            cypherseed.save(self.base_dir + self.filename +self.version + '_'+ self.code_id + '.png')
            self.bcrypt(self.c_dialog)
        return cypherseed
Exemplo n.º 11
0
 def get_library_not_available_message(self) -> str:
     if hasattr(self, 'libraries_available_message'):
         message = self.libraries_available_message
     else:
         message = _("Missing libraries for {}.").format(self.name)
     message += '\n' + _("Make sure you install it with python3")
     return message
Exemplo n.º 12
0
    def multi_mpk_dialog(self, xpub_hot, n):
        vbox = QVBoxLayout()
        scroll = QScrollArea()
        scroll.setEnabled(True)
        scroll.setWidgetResizable(True)
        vbox.addWidget(scroll)

        w = QWidget()
        scroll.setWidget(w)

        innerVbox = QVBoxLayout()
        w.setLayout(innerVbox)

        vbox0 = seed_dialog.show_seed_box(MSG_SHOW_MPK, xpub_hot, 'hot')
        innerVbox.addLayout(vbox0)
        entries = []
        for i in range(n):
            msg = _("Please enter the master public key of cosigner") + ' %d'%(i+1)
            vbox2, seed_e2 = seed_dialog.enter_seed_box(msg, self, 'cold')
            innerVbox.addLayout(vbox2)
            entries.append(seed_e2)
        vbox.addStretch(1)
        button = OkButton(self, _('Next'))
        vbox.addLayout(Buttons(CancelButton(self), button))
        button.setEnabled(False)
        f = lambda: button.setEnabled( map(lambda e: Wallet.is_xpub(self.get_seed_text(e)), entries) == [True]*len(entries))
        for e in entries:
            e.textChanged.connect(f)
        self.set_layout(vbox)
        if not self.exec_():
            return
        return map(lambda e: self.get_seed_text(e), entries)
Exemplo n.º 13
0
 def get_tooltip(self, pos, fee_rate):
     mempool = self.config.use_mempool_fees()
     target, estimate = self.config.get_fee_text(pos, self.dyn, mempool, fee_rate)
     if self.dyn:
         return _('Target') + ': ' + target + '\n' + _('Current rate') + ': ' + estimate
     else:
         return _('Fixed rate') + ': ' + target + '\n' + _('Estimate') + ': ' + estimate
Exemplo n.º 14
0
 def toggle_passphrase(self):
     if self.features.passphrase_protection:
         self.msg = _("Confirm on your {} device to disable passphrases")
     else:
         self.msg = _("Confirm on your {} device to enable passphrases")
     enabled = not self.features.passphrase_protection
     self.apply_settings(use_passphrase=enabled)
Exemplo n.º 15
0
    def save_current_masternode(self, as_new=False):
        """Save the masternode that is being viewed.

        If as_new is True, a new masternode will be created.
        """
        delegate_privkey = str(self.masternode_editor.delegate_key_edit.text())
        if not delegate_privkey:
            QMessageBox.warning(self, _('Warning'), _('Delegate private key is empty.'))
            return

        try:
            delegate_pubkey = self.manager.import_masternode_delegate(delegate_privkey)
        except Exception:
            # Show an error if the private key is invalid and not an empty string.
            if delegate_privkey:
                QMessageBox.warning(self, _('Warning'), _('Ignoring invalid delegate private key.'))
            delegate_pubkey = ''

        alias = str(self.masternode_editor.alias_edit.text())
        # Construct a new masternode.
        if as_new:
            kwargs = self.masternode_editor.get_masternode_args()
            kwargs['delegate_key'] = delegate_pubkey
            del kwargs['vin']
            self.mapper.revert()
            self.masternodes_widget.add_masternode(MasternodeAnnounce(**kwargs))
        else:
            self.mapper.submit()
        self.manager.save()
        self.masternodes_widget.select_masternode(alias)
Exemplo n.º 16
0
    def mouseReleaseEvent(self, event):
        dialog = QDialog(self)
        dialog.setWindowTitle(_('Electrum update'))
        dialog.setModal(1)

        main_layout = QGridLayout()
        main_layout.addWidget(QLabel(_("A new version of Electrum is available:")+" " + self.latest_version), 0,0,1,3)

        ignore_version = QPushButton(_("Ignore this version"))
        ignore_version.clicked.connect(self.ignore_this_version)

        ignore_all_versions = QPushButton(_("Ignore all versions"))
        ignore_all_versions.clicked.connect(self.ignore_all_version)

        open_website = QPushButton(_("Goto download page"))
        open_website.clicked.connect(self.open_website)

        main_layout.addWidget(ignore_version, 1, 0)
        main_layout.addWidget(ignore_all_versions, 1, 1)
        main_layout.addWidget(open_website, 1, 2)

        dialog.setLayout(main_layout)

        self.dialog = dialog

        if not dialog.exec_(): return
Exemplo n.º 17
0
    def sign_announce(self, alias):
        """Sign an announce for alias. This is called by SignAnnounceWidget."""
        pw = None
        if self.manager.wallet.has_password():
            pw = self.gui.password_dialog(msg=_('Please enter your password to activate masternode "%s".' % alias))
            if pw is None:
                return

        self.sign_announce_widget.sign_button.setEnabled(False)

        def sign_thread():
            return self.manager.sign_announce(alias, pw)

        def on_sign_successful(mn):
            self.print_msg('Successfully signed Masternode Announce.')
            self.send_announce(alias)
        # Proceed to broadcasting the announcement, or re-enable the button.
        def on_sign_error(err):
            self.print_error('Error signing MasternodeAnnounce:')
            # Print traceback information to error log.
            self.print_error(''.join(traceback.format_tb(err[2])))
            self.print_error(''.join(traceback.format_exception_only(err[0], err[1])))
            self.sign_announce_widget.sign_button.setEnabled(True)

        util.WaitingDialog(self, _('Signing Masternode Announce...'), sign_thread, on_sign_successful, on_sign_error)
Exemplo n.º 18
0
    def __init__(self, address, parent):
        self.address = address
        self.parent = parent
        self.config = parent.config
        self.wallet = parent.wallet
        self.app = parent.app
        self.saved = True

        QDialog.__init__(self)
        self.setMinimumWidth(700)
        self.setWindowTitle(_("Address"))
        self.setModal(1)
        vbox = QVBoxLayout()
        self.setLayout(vbox)

        vbox.addWidget(QLabel(_("Address:")))
        self.addr_e = ButtonsLineEdit(self.address)
        self.addr_e.addCopyButton(self.app)
        self.addr_e.addButton(":icons/qrcode.png", self.show_qr, _("Show QR Code"))
        self.addr_e.setReadOnly(True)
        vbox.addWidget(self.addr_e)

        vbox.addWidget(QLabel(_("History")))
        self.hw = HistoryWidget(self.parent)
        vbox.addWidget(self.hw)

        vbox.addStretch(1)
        vbox.addLayout(Buttons(CloseButton(self)))
        self.format_amount = self.parent.format_amount

        h = self.wallet.get_history([self.address])
        self.hw.update(h)
Exemplo n.º 19
0
    def __init__(self, parent):
        super(MatrixDialog, self).__init__(parent)
        self.setWindowTitle(_("Trezor Matrix Recovery"))
        self.num = 9
        self.loop = QEventLoop()

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(MATRIX_RECOVERY))

        grid = QGridLayout()
        grid.setSpacing(0)
        self.char_buttons = []
        for y in range(3):
            for x in range(3):
                button = QPushButton('?')
                button.clicked.connect(partial(self.process_key, ord('1') + y * 3 + x))
                grid.addWidget(button, 3 - y, x)
                self.char_buttons.append(button)
        vbox.addLayout(grid)

        self.backspace_button = QPushButton("<=")
        self.backspace_button.clicked.connect(partial(self.process_key, Qt.Key_Backspace))
        self.cancel_button = QPushButton(_("Cancel"))
        self.cancel_button.clicked.connect(partial(self.process_key, Qt.Key_Escape))
        buttons = Buttons(self.backspace_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Exemplo n.º 20
0
 def on_change_hist(checked):
     if checked:
         self.config.set_key('history_rates', 'checked')
         self.request_history_rates()
     else:
         self.config.set_key('history_rates', 'unchecked')
         self.win.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance')] )
         self.win.history_list.setColumnCount(5)
Exemplo n.º 21
0
 def __init__(self, text="", allow_multi=False):
     ButtonsTextEdit.__init__(self, text)
     self.allow_multi = allow_multi
     self.setReadOnly(0)
     self.addButton(":icons/file.png", self.file_input, _("Read file"))
     icon = ":icons/qrcode.png"
     self.addButton(icon, self.qr_input, _("Read QR code"))
     run_hook('scan_text_edit', self)
Exemplo n.º 22
0
 def export(self):
     name = 'signed_%s.txn' % (self.tx.txid()[0:8]) if self.tx.is_complete() else 'unsigned.txn'
     fileName = self.main_window.getSaveFileName(_("Select where to save your signed transaction"), name, "*.txn")
     if fileName:
         with open(fileName, "w+") as f:
             f.write(json.dumps(self.tx.as_dict(), indent=4) + '\n')
         self.show_message(_("Transaction exported successfully"))
         self.saved = True
Exemplo n.º 23
0
 def set_pin(self, remove):
     if remove:
         self.msg = _("Confirm on your {} device to disable PIN protection")
     elif self.features.pin_protection:
         self.msg = _("Confirm on your {} device to change your PIN")
     else:
         self.msg = _("Confirm on your {} device to set a PIN")
     self.change_pin(remove)
Exemplo n.º 24
0
    def history_tab_update(self):
        # Disabled.
        return

        if self.config.get('history_rates') != "checked":
            return
        if not self.resp_hist:
            return
        if not self.wallet:
            return

        self.win.is_edit = True
        self.win.history_list.setColumnCount(6)
        self.win.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance'), _('Fiat Amount')] )
        root = self.win.history_list.invisibleRootItem()
        childcount = root.childCount()
        for i in range(childcount):
            item = root.child(i)
            try:
                tx_info = self.tx_list[str(item.data(0, Qt.UserRole).toPyObject())]
            except Exception:
                newtx = self.wallet.get_history()
                v = newtx[[x[0] for x in newtx].index(str(item.data(0, Qt.UserRole).toPyObject()))][2]
                tx_info = {'timestamp':int(time.time()), 'value': v}
                pass
            tx_time = int(tx_info['timestamp'])
            tx_value = Decimal(str(tx_info['value'])) / COIN
            if self.cur_exchange == "CoinDesk":
                tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d')
                try:
                    tx_fiat_val = "%.2f %s" % (tx_value * Decimal(self.resp_hist['bpi'][tx_time_str]), "USD")
                except KeyError:
                    tx_fiat_val = "%.2f %s" % (self.btc_rate * Decimal(str(tx_info['value']))/COIN , "USD")
            elif self.cur_exchange == "Winkdex":
                tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d') + "T16:00:00-04:00"
                try:
                    tx_rate = self.resp_hist[[x['timestamp'] for x in self.resp_hist].index(tx_time_str)]['price']
                    tx_fiat_val = "%.2f %s" % (tx_value * Decimal(tx_rate)/Decimal("100.0"), "USD")
                except ValueError:
                    tx_fiat_val = "%.2f %s" % (self.btc_rate * Decimal(tx_info['value'])/COIN , "USD")
                except KeyError:
                    tx_fiat_val = _("No data")
            elif self.cur_exchange == "BitcoinVenezuela":
                tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d')
                try:
                    num = self.resp_hist[tx_time_str].replace(',','')
                    tx_fiat_val = "%.2f %s" % (tx_value * Decimal(num), self.fiat_unit())
                except KeyError:
                    tx_fiat_val = _("No data")

            tx_fiat_val = " "*(12-len(tx_fiat_val)) + tx_fiat_val
            item.setText(5, tx_fiat_val)
            item.setFont(5, QFont(MONOSPACE_FONT))
            if Decimal(str(tx_info['value'])) < 0:
                item.setForeground(5, QBrush(QColor("#BC1E1E")))

        self.win.history_list.setColumnWidth(5, 120)
        self.win.is_edit = False
Exemplo n.º 25
0
 def verify_seed(self, seed, sid, func=None):
     r = self.enter_seed_dialog(MSG_VERIFY_SEED, sid, func)
     if not r:
         return
     if prepare_seed(r) != prepare_seed(seed):
         QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
         return False
     else:
         return True
Exemplo n.º 26
0
    def add_io(self, vbox):

        if self.tx.locktime > 0:
            vbox.addWidget(QLabel("LockTime: %d\n" % self.tx.locktime))

        vbox.addWidget(QLabel(_("Inputs")))

        ext = QTextCharFormat()
        rec = QTextCharFormat()
        rec.setBackground(QBrush(QColor("lightgreen")))
        rec.setToolTip(_("Wallet receive address"))
        chg = QTextCharFormat()
        chg.setBackground(QBrush(QColor("yellow")))
        chg.setToolTip(_("Wallet change address"))

        def text_format(addr):
            if self.wallet.is_mine(addr):
                return chg if self.wallet.is_change(addr) else rec
            return ext

        i_text = QTextEdit()
        i_text.setFont(QFont(MONOSPACE_FONT))
        i_text.setReadOnly(True)
        i_text.setMaximumHeight(100)
        cursor = i_text.textCursor()
        for x in self.tx.inputs:
            if x.get('is_coinbase'):
                cursor.insertText('coinbase')
            else:
                prevout_hash = x.get('prevout_hash')
                prevout_n = x.get('prevout_n')
                cursor.insertText(prevout_hash[0:8] + '...', ext)
                cursor.insertText(prevout_hash[-8:] + ":%-4d " % prevout_n, ext)
                addr = x.get('address')
                if addr == "(pubkey)":
                    _addr = self.wallet.find_pay_to_pubkey_address(prevout_hash, prevout_n)
                    if _addr:
                        addr = _addr
                if addr is None:
                    addr = _('unknown')
                cursor.insertText(addr, text_format(addr))
            cursor.insertBlock()

        vbox.addWidget(i_text)
        vbox.addWidget(QLabel(_("Outputs")))
        o_text = QTextEdit()
        o_text.setFont(QFont(MONOSPACE_FONT))
        o_text.setReadOnly(True)
        o_text.setMaximumHeight(100)
        cursor = o_text.textCursor()
        for addr, v in self.tx.get_outputs():
            cursor.insertText(addr, text_format(addr))
            if v is not None:
                cursor.insertText('\t', ext)
                cursor.insertText(self.parent.format_amount(v, whitespaces = True), ext)
            cursor.insertBlock()
        vbox.addWidget(o_text)
Exemplo n.º 27
0
 def setup_device(self, device_info, wizard, purpose):
     devmgr = self.device_manager()
     device_id = device_info.device.id_
     client = devmgr.client_by_id(device_id)
     if client is None:
         raise Exception(_('Failed to create a client for this device.') + '\n' +
                         _('Make sure it is in the correct state.'))
     client.handler = self.create_handler(wizard)
     client.get_xpub("m/44'/5'", 'standard') # TODO replace by direct derivation once Nano S > 1.1
Exemplo n.º 28
0
    def __init__(self, seed=None, title=None, icon=True, msg=None, options=None,
                 is_seed=None, passphrase=None, parent=None, for_seed_words=True):
        QVBoxLayout.__init__(self)
        self.parent = parent
        self.options = options
        if title:
            self.addWidget(WWLabel(title))
        if seed:  # "read only", we already have the text
            if for_seed_words:
                self.seed_e = ButtonsTextEdit()
            else:  # e.g. xpub
                self.seed_e = ShowQRTextEdit()
            self.seed_e.setReadOnly(True)
            self.seed_e.setText(seed)
        else:  # we expect user to enter text
            assert for_seed_words
            self.seed_e = CompletionTextEdit()
            self.seed_e.setTabChangesFocus(False)  # so that tab auto-completes
            self.is_seed = is_seed
            self.saved_is_seed = self.is_seed
            self.seed_e.textChanged.connect(self.on_edit)
            self.initialize_completer()

        self.seed_e.setMaximumHeight(75)
        hbox = QHBoxLayout()
        if icon:
            logo = QLabel()
            logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(64, mode=Qt.SmoothTransformation))
            logo.setMaximumWidth(60)
            hbox.addWidget(logo)
        hbox.addWidget(self.seed_e)
        self.addLayout(hbox)
        hbox = QHBoxLayout()
        hbox.addStretch(1)
        self.seed_type_label = QLabel('')
        hbox.addWidget(self.seed_type_label)

        # options
        self.is_bip39 = False
        self.is_ext = False
        if options:
            opt_button = EnterButton(_('Options'), self.seed_options)
            hbox.addWidget(opt_button)
            self.addLayout(hbox)
        if passphrase:
            hbox = QHBoxLayout()
            passphrase_e = QLineEdit()
            passphrase_e.setText(passphrase)
            passphrase_e.setReadOnly(True)
            hbox.addWidget(QLabel(_("Your seed extension is") + ':'))
            hbox.addWidget(passphrase_e)
            self.addLayout(hbox)
        self.addStretch(1)
        self.seed_warning = WWLabel('')
        if msg:
            self.seed_warning.setText(seed_warning_msg(seed))
        self.addWidget(self.seed_warning)
Exemplo n.º 29
0
 def on_update(self):
     self.wallet = self.parent.wallet
     item = self.currentItem()
     current_address = item.data(0, Qt.UserRole) if item else None
     if self.show_change == 1:
         addr_list = self.wallet.get_receiving_addresses()
     elif self.show_change == 2:
         addr_list = self.wallet.get_change_addresses()
     else:
         addr_list = self.wallet.get_addresses()
     self.clear()
     fx = self.parent.fx
     for address in addr_list:
         num = self.wallet.get_address_history_len(address)
         label = self.wallet.labels.get(address, '')
         c, u, x = self.wallet.get_addr_balance(address)
         balance = c + u + x
         is_used_and_empty = self.wallet.is_used(address) and balance == 0
         if self.show_used == 1 and (balance or is_used_and_empty):
             continue
         if self.show_used == 2 and balance == 0:
             continue
         if self.show_used == 3 and not is_used_and_empty:
             continue
         balance_text = self.parent.format_amount(balance, whitespaces=True)
         # create item
         if fx and fx.get_fiat_address_config():
             rate = fx.exchange_rate()
             fiat_balance = fx.value_str(balance, rate)
             address_item = SortableTreeWidgetItem(['', address, label, balance_text, fiat_balance, "%d"%num])
         else:
             address_item = SortableTreeWidgetItem(['', address, label, balance_text, "%d"%num])
         # align text and set fonts
         for i in range(address_item.columnCount()):
             address_item.setTextAlignment(i, Qt.AlignVCenter)
             if i not in (0, 2):
                 address_item.setFont(i, QFont(MONOSPACE_FONT))
         if fx and fx.get_fiat_address_config():
             address_item.setTextAlignment(4, Qt.AlignRight | Qt.AlignVCenter)
         # setup column 0
         if self.wallet.is_change(address):
             address_item.setText(0, _('change'))
             address_item.setBackground(0, ColorScheme.YELLOW.as_color(True))
         else:
             address_item.setText(0, _('receiving'))
             address_item.setBackground(0, ColorScheme.GREEN.as_color(True))
         address_item.setData(0, Qt.UserRole, address)  # column 0; independent from address column
         # setup column 1
         if self.wallet.is_frozen(address):
             address_item.setBackground(1, ColorScheme.BLUE.as_color(True))
         if self.wallet.is_beyond_limit(address):
             address_item.setBackground(1, ColorScheme.RED.as_color(True))
         # add item
         self.addChild(address_item)
         if address == current_address:
             self.setCurrentItem(address_item)
Exemplo n.º 30
0
 def wipe_device():
     wallet = window.wallet
     if wallet and sum(wallet.get_balance()):
         title = _("Confirm Device Wipe")
         msg = _("Are you SURE you want to wipe the device?\n"
                 "Your wallet still has Dash coins in it!")
         if not self.question(msg, title=title,
                              icon=QMessageBox.Critical):
             return
     invoke_client('wipe_device', unpair_after=True)
Exemplo n.º 31
0
 def on_filename(filename):
     # FIXME? "filename" might contain ".." (etc) and hence sketchy path traversals are possible
     nonlocal temp_storage
     temp_storage = None
     msg = None
     if filename:
         path = os.path.join(wallet_folder, filename)
         wallet_from_memory = get_wallet_from_daemon(path)
         try:
             if wallet_from_memory:
                 temp_storage = wallet_from_memory.storage  # type: Optional[WalletStorage]
             else:
                 temp_storage = WalletStorage(path)
         except (StorageReadWriteError, WalletFileException) as e:
             msg = _('Cannot read file') + f'\n{repr(e)}'
         except Exception as e:
             self.logger.exception('')
             msg = _('Cannot read file') + f'\n{repr(e)}'
     else:
         msg = ""
     self.next_button.setEnabled(temp_storage is not None)
     user_needs_to_enter_password = False
     if temp_storage:
         if not temp_storage.file_exists():
             msg =_("This file does not exist.") + '\n' \
                   + _("Press 'Next' to create this wallet, or choose another file.")
         elif not wallet_from_memory:
             if temp_storage.is_encrypted_with_user_pw():
                 msg = _("This file is encrypted with a password.") + '\n' \
                       + _('Enter your password or choose another file.')
                 user_needs_to_enter_password = True
             elif temp_storage.is_encrypted_with_hw_device():
                 msg = _("This file is encrypted using a hardware device.") + '\n' \
                       + _("Press 'Next' to choose device to decrypt.")
             else:
                 msg = _("Press 'Next' to open this wallet.")
         else:
             msg = _("This file is already open in memory.") + "\n" \
                 + _("Press 'Next' to create/focus window.")
     if msg is None:
         msg = _('Cannot read file')
     msg_label.setText(msg)
     widget_create_new.setVisible(
         bool(temp_storage and temp_storage.file_exists()))
     if user_needs_to_enter_password:
         pw_label.show()
         pw_e.show()
         pw_e.setFocus()
     else:
         pw_label.hide()
         pw_e.hide()
Exemplo n.º 32
0
 def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> QVariant:
     assert index.isValid()
     col = index.column()
     coin_item = index.internalPointer()
     address = coin_item['address']
     is_frozen_addr = coin_item['is_frozen_addr']
     is_frozen_coin = coin_item['is_frozen_coin']
     height = coin_item['height']
     time_str = ''
     if self.view.config.get('show_utxo_time', False):
         prevout_timestamp = coin_item['prevout_timestamp']
         time_str = (format_time(prevout_timestamp)
                     if prevout_timestamp < math.inf else _('unknown'))
     outpoint = coin_item['outpoint']
     out_short = coin_item['out_short']
     label = coin_item['label']
     balance = coin_item['balance']
     ps_rounds = coin_item['ps_rounds']
     is_ps_ks = coin_item['is_ps_ks']
     if ps_rounds is None:
         ps_rounds = 'N/A'
     else:
         ps_rounds = ps_coin_rounds_str(ps_rounds)
     if (role == self.view.ROLE_CLIPBOARD_DATA
             and col == UTXOColumns.OUTPOINT):
         return QVariant(outpoint)
     elif role == Qt.ToolTipRole:
         if col == UTXOColumns.ADDRESS and is_frozen_addr:
             return QVariant(_('Address is frozen'))
         elif col == UTXOColumns.OUTPOINT and is_frozen_coin:
             return QVariant(f'{outpoint}\n{_("Coin is frozen")}')
         elif outpoint in (self.view._spend_set or set()):
             if col == UTXOColumns.OUTPOINT:
                 return QVariant(f'{outpoint}\n{SELECTED_TO_SPEND_TOOLTIP}')
             else:
                 return QVariant(SELECTED_TO_SPEND_TOOLTIP)
         elif col == UTXOColumns.OUTPOINT:
             return QVariant(outpoint)
     elif role not in (Qt.DisplayRole, Qt.EditRole):
         if role == Qt.TextAlignmentRole:
             if col in [
                     UTXOColumns.DATE, UTXOColumns.AMOUNT,
                     UTXOColumns.HEIGHT, UTXOColumns.PS_ROUNDS,
                     UTXOColumns.KEYSTORE_TYPE
             ]:
                 return QVariant(Qt.AlignRight | Qt.AlignVCenter)
             else:
                 return QVariant(Qt.AlignVCenter)
         elif role == Qt.FontRole:
             return QVariant(QFont(MONOSPACE_FONT))
         elif role == Qt.BackgroundRole:
             if col == UTXOColumns.ADDRESS and is_frozen_addr:
                 return QVariant(ColorScheme.BLUE.as_color(True))
             elif col == UTXOColumns.OUTPOINT and is_frozen_coin:
                 return QVariant(ColorScheme.BLUE.as_color(True))
             elif outpoint in (self.view._spend_set or set()):
                 return QVariant(ColorScheme.GREEN.as_color(True))
     elif col == UTXOColumns.DATE:
         return QVariant(time_str)
     elif col == UTXOColumns.OUTPOINT:
         return QVariant(out_short)
     elif col == UTXOColumns.ADDRESS:
         return QVariant(address)
     elif col == UTXOColumns.LABEL:
         return QVariant(label)
     elif col == UTXOColumns.AMOUNT:
         return QVariant(balance)
     elif col == UTXOColumns.HEIGHT:
         return QVariant(height)
     elif col == UTXOColumns.PS_ROUNDS:
         return QVariant(ps_rounds)
     elif col == UTXOColumns.KEYSTORE_TYPE:
         return QVariant(_('PS Keystore') if is_ps_ks else _('Main'))
     else:
         return QVariant()
Exemplo n.º 33
0
from PyQt5.QtCore import (pyqtSignal, Qt, QModelIndex, QVariant,
                          QAbstractItemModel, QItemSelectionModel)
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (QAbstractItemView, QHeaderView, QComboBox, QLabel,
                             QMenu)

from electrum_dash.i18n import _
from electrum_dash.transaction import PartialTxInput
from electrum_dash.dash_ps_util import (PSCoinRounds, sort_utxos_by_ps_rounds,
                                        ps_coin_rounds_str)
from electrum_dash.logging import Logger
from electrum_dash.util import profiler, format_time

from .util import MyTreeView, ColorScheme, MONOSPACE_FONT, EnterButton, GetDataThread

SELECTED_TO_SPEND_TOOLTIP = _('Coin selected to be spent')


class PSStateFilter(IntEnum):
    ALL = 0
    PS = 1
    PS_OTHER = 2
    REGULAR = 3

    def ui_text(self) -> str:
        return {
            self.ALL: _('All'),
            self.PS: _('PrivateSend'),
            self.PS_OTHER: _('PS Other coins'),
            self.REGULAR: _('Regular'),
        }[self]
Exemplo n.º 34
0
from electrum_dash import Wallet, WalletStorage
from electrum_dash.util import UserCancelled, InvalidPassword
from electrum_dash.base_wizard import BaseWizard
from electrum_dash.i18n import _

from .seed_dialog import SeedLayout, KeysLayout
from .network_dialog import NetworkChoiceLayout
from .util import *
from .password_dialog import PasswordLayout, PW_NEW


class GoBack(Exception):
    pass


MSG_GENERATING_WAIT = _(
    "Electrum-DASH is generating your addresses, please wait...")
MSG_ENTER_ANYTHING = _("Please enter a seed phrase, a master key, a list of "
                       "Dash addresses, or a list of private keys")
MSG_ENTER_SEED_OR_MPK = _(
    "Please enter a seed phrase or a master key (xpub or xprv):")
MSG_COSIGNER = _("Please enter the master public key of cosigner #%d:")
MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\
                     + _("Leave this field empty if you want to disable encryption.")
MSG_RESTORE_PASSPHRASE = \
    _("Please enter your seed derivation passphrase. "
      "Note: this is NOT your encryption password. "
      "Leave this field empty if you did not use one or are unsure.")


class CosignWidget(QWidget):
    size = 120
Exemplo n.º 35
0
 def on_n(n):
     n_label.setText(_('From {0} cosigners').format(n))
     cw.set_n(n)
     m_edit.setMaximum(n)
     backup_warning_label.setVisible(cw.m != cw.n)
Exemplo n.º 36
0
class RequestList(MyTreeView):
    class Columns(IntEnum):
        DATE = 0
        DESCRIPTION = 1
        AMOUNT = 2
        STATUS = 3

    headers = {
        Columns.DATE: _('Date'),
        Columns.DESCRIPTION: _('Description'),
        Columns.AMOUNT: _('Amount'),
        Columns.STATUS: _('Status'),
    }
    filter_columns = [Columns.DATE, Columns.DESCRIPTION, Columns.AMOUNT]

    def __init__(self, parent: 'ElectrumWindow'):
        super().__init__(parent,
                         self.create_menu,
                         stretch_column=self.Columns.DESCRIPTION,
                         editable_columns=[])
        self.wallet = self.parent.wallet
        self.std_model = QStandardItemModel(self)
        self.proxy = MySortModel(self, sort_role=ROLE_SORT_ORDER)
        self.proxy.setSourceModel(self.std_model)
        self.setModel(self.proxy)
        self.setSortingEnabled(True)
        self.selectionModel().currentRowChanged.connect(self.item_changed)
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.update()

    def select_key(self, key):
        for i in range(self.model().rowCount()):
            item = self.model().index(i, self.Columns.DATE)
            row_key = item.data(ROLE_KEY)
            if key == row_key:
                self.selectionModel().setCurrentIndex(
                    item, QItemSelectionModel.SelectCurrent
                    | QItemSelectionModel.Rows)
                break

    def item_changed(self, idx: Optional[QModelIndex]):
        if idx is None:
            self.parent.receive_payreq_e.setText('')
            self.parent.receive_address_e.setText('')
            return
        if not idx.isValid():
            return
        # TODO use siblingAtColumn when min Qt version is >=5.11
        item = self.item_from_index(idx.sibling(idx.row(), self.Columns.DATE))
        key = item.data(ROLE_KEY)
        req = self.wallet.get_request(key)
        if req is None:
            self.update()
            return
        self.parent.receive_payreq_e.setText(
            self.parent.wallet.get_request_URI(req))
        self.parent.receive_address_e.setText(req.get_address())
        self.parent.receive_payreq_e.repaint()  # macOS hack (similar to #4777)
        self.parent.receive_address_e.repaint(
        )  # macOS hack (similar to #4777)

    def clearSelection(self):
        super().clearSelection()
        self.selectionModel().clearCurrentIndex()

    def refresh_status(self):
        m = self.std_model
        for r in range(m.rowCount()):
            idx = m.index(r, self.Columns.STATUS)
            date_idx = idx.sibling(idx.row(), self.Columns.DATE)
            date_item = m.itemFromIndex(date_idx)
            status_item = m.itemFromIndex(idx)
            key = date_item.data(ROLE_KEY)
            req = self.wallet.get_request(key)
            if req:
                status = self.parent.wallet.get_request_status(key)
                status_str = req.get_status_str(status)
                status_item.setText(status_str)
                status_item.setIcon(read_QIcon(pr_icons.get(status)))

    def update_item(self, key, invoice: Invoice):
        model = self.std_model
        for row in range(0, model.rowCount()):
            item = model.item(row, 0)
            if item.data(ROLE_KEY) == key:
                break
        else:
            return
        status_item = model.item(row, self.Columns.STATUS)
        status = self.parent.wallet.get_request_status(key)
        status_str = invoice.get_status_str(status)
        status_item.setText(status_str)
        status_item.setIcon(read_QIcon(pr_icons.get(status)))

    def update(self):
        # not calling maybe_defer_update() as it interferes with conditional-visibility
        self.parent.update_receive_address_styling()
        self.proxy.setDynamicSortFilter(
            False)  # temp. disable re-sorting after every change
        self.std_model.clear()
        self.update_headers(self.__class__.headers)
        for req in self.wallet.get_unpaid_requests():
            key = self.wallet.get_key_for_receive_request(req)
            status = self.parent.wallet.get_request_status(key)
            status_str = req.get_status_str(status)
            request_type = req.type
            timestamp = req.time
            amount = req.get_amount_sat()
            message = req.message
            date = format_time(timestamp)
            amount_str = self.parent.format_amount(amount) if amount else ""
            labels = [date, message, amount_str, status_str]
            icon = read_QIcon("dashcoin.png")
            tooltip = 'onchain request'
            items = [QStandardItem(e) for e in labels]
            self.set_editability(items)
            items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE)
            items[self.Columns.DATE].setData(key, ROLE_KEY)
            items[self.Columns.DATE].setData(timestamp, ROLE_SORT_ORDER)
            items[self.Columns.DATE].setIcon(icon)
            items[self.Columns.STATUS].setIcon(read_QIcon(
                pr_icons.get(status)))
            items[self.Columns.DATE].setToolTip(tooltip)
            self.std_model.insertRow(self.std_model.rowCount(), items)
        self.filter()
        self.proxy.setDynamicSortFilter(True)
        # sort requests by date
        self.sortByColumn(self.Columns.DATE, Qt.DescendingOrder)
        # hide list if empty
        if self.parent.isVisible():
            b = self.std_model.rowCount() > 0
            self.setVisible(b)
            self.parent.receive_requests_label.setVisible(b)
            if not b:
                # list got hidden, so selected item should also be cleared:
                self.item_changed(None)

    def create_menu(self, position):
        items = self.selected_in_column(0)
        if len(items) > 1:
            keys = [item.data(ROLE_KEY) for item in items]
            menu = QMenu(self)
            menu.addAction(_("Delete requests"),
                           lambda: self.parent.delete_requests(keys))
            menu.exec_(self.viewport().mapToGlobal(position))
            return
        idx = self.indexAt(position)
        # TODO use siblingAtColumn when min Qt version is >=5.11
        item = self.item_from_index(idx.sibling(idx.row(), self.Columns.DATE))
        if not item:
            return
        key = item.data(ROLE_KEY)
        req = self.wallet.get_request(key)
        if req is None:
            self.update()
            return
        menu = QMenu(self)
        self.add_copy_menu(menu, idx)
        URI = self.wallet.get_request_URI(req)
        menu.addAction(_("Copy Request"),
                       lambda: self.parent.do_copy(URI, title='Dash URI'))
        menu.addAction(
            _("Copy Address"),
            lambda: self.parent.do_copy(req.get_address(),
                                        title='Dash Address'))
        #if 'view_url' in req:
        #    menu.addAction(_("View in web browser"), lambda: webopen(req['view_url']))
        menu.addAction(_("Delete"), lambda: self.parent.delete_requests([key]))
        run_hook('receive_list_menu', self.parent, menu, key)
        menu.exec_(self.viewport().mapToGlobal(position))
Exemplo n.º 37
0
    def select_storage(
            self, path,
            get_wallet_from_daemon) -> Tuple[str, Optional[WalletStorage]]:

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel(_('Wallet') + ':'))
        name_e = QLineEdit()
        hbox.addWidget(name_e)
        button = QPushButton(_('Choose...'))
        hbox.addWidget(button)
        vbox.addLayout(hbox)

        msg_label = WWLabel('')
        vbox.addWidget(msg_label)
        hbox2 = QHBoxLayout()
        pw_e = PasswordLineEdit('', self)
        pw_e.setFixedWidth(17 * char_width_in_lineedit())
        pw_label = QLabel(_('Password') + ':')
        hbox2.addWidget(pw_label)
        hbox2.addWidget(pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)

        vbox.addSpacing(50)
        vbox_create_new = QVBoxLayout()
        vbox_create_new.addWidget(QLabel(_('Alternatively') + ':'),
                                  alignment=Qt.AlignLeft)
        button_create_new = QPushButton(_('Create New Wallet'))
        button_create_new.setMinimumWidth(120)
        vbox_create_new.addWidget(button_create_new, alignment=Qt.AlignLeft)
        widget_create_new = QWidget()
        widget_create_new.setLayout(vbox_create_new)
        vbox_create_new.setContentsMargins(0, 0, 0, 0)
        vbox.addWidget(widget_create_new)

        self.set_layout(vbox, title=_('Dash Electrum wallet'))

        temp_storage = None  # type: Optional[WalletStorage]
        wallet_folder = os.path.dirname(path)

        def on_choose():
            path, __ = QFileDialog.getOpenFileName(self,
                                                   "Select your wallet file",
                                                   wallet_folder)
            if path:
                name_e.setText(path)

        def on_filename(filename):
            # FIXME? "filename" might contain ".." (etc) and hence sketchy path traversals are possible
            nonlocal temp_storage
            temp_storage = None
            msg = None
            if filename:
                path = os.path.join(wallet_folder, filename)
                wallet_from_memory = get_wallet_from_daemon(path)
                try:
                    if wallet_from_memory:
                        temp_storage = wallet_from_memory.storage  # type: Optional[WalletStorage]
                    else:
                        temp_storage = WalletStorage(path)
                except (StorageReadWriteError, WalletFileException) as e:
                    msg = _('Cannot read file') + f'\n{repr(e)}'
                except Exception as e:
                    self.logger.exception('')
                    msg = _('Cannot read file') + f'\n{repr(e)}'
            else:
                msg = ""
            self.next_button.setEnabled(temp_storage is not None)
            user_needs_to_enter_password = False
            if temp_storage:
                if not temp_storage.file_exists():
                    msg =_("This file does not exist.") + '\n' \
                          + _("Press 'Next' to create this wallet, or choose another file.")
                elif not wallet_from_memory:
                    if temp_storage.is_encrypted_with_user_pw():
                        msg = _("This file is encrypted with a password.") + '\n' \
                              + _('Enter your password or choose another file.')
                        user_needs_to_enter_password = True
                    elif temp_storage.is_encrypted_with_hw_device():
                        msg = _("This file is encrypted using a hardware device.") + '\n' \
                              + _("Press 'Next' to choose device to decrypt.")
                    else:
                        msg = _("Press 'Next' to open this wallet.")
                else:
                    msg = _("This file is already open in memory.") + "\n" \
                        + _("Press 'Next' to create/focus window.")
            if msg is None:
                msg = _('Cannot read file')
            msg_label.setText(msg)
            widget_create_new.setVisible(
                bool(temp_storage and temp_storage.file_exists()))
            if user_needs_to_enter_password:
                pw_label.show()
                pw_e.show()
                pw_e.setFocus()
            else:
                pw_label.hide()
                pw_e.hide()

        button.clicked.connect(on_choose)
        button_create_new.clicked.connect(
            partial(name_e.setText, get_new_wallet_name(wallet_folder)))
        name_e.textChanged.connect(on_filename)
        name_e.setText(os.path.basename(path))

        def run_user_interaction_loop():
            while True:
                if self.loop.exec_() != 2:  # 2 = next
                    raise UserCancelled()
                assert temp_storage
                if temp_storage.file_exists(
                ) and not temp_storage.is_encrypted():
                    break
                if not temp_storage.file_exists():
                    break
                wallet_from_memory = get_wallet_from_daemon(temp_storage.path)
                if wallet_from_memory:
                    raise WalletAlreadyOpenInMemory(wallet_from_memory)
                if temp_storage.file_exists() and temp_storage.is_encrypted():
                    if temp_storage.is_encrypted_with_user_pw():
                        password = pw_e.text()
                        try:
                            temp_storage.decrypt(password)
                            break
                        except InvalidPassword as e:
                            self.show_message(title=_('Error'), msg=str(e))
                            continue
                        except BaseException as e:
                            self.logger.exception('')
                            self.show_message(title=_('Error'), msg=repr(e))
                            raise UserCancelled()
                    elif temp_storage.is_encrypted_with_hw_device():
                        try:
                            self.run('choose_hw_device',
                                     HWD_SETUP_DECRYPT_WALLET,
                                     storage=temp_storage)
                        except InvalidPassword as e:
                            self.show_message(
                                title=_('Error'),
                                msg=
                                _('Failed to decrypt using this hardware device.'
                                  ) + '\n' +
                                _('If you use a passphrase, make sure it is correct.'
                                  ))
                            self.reset_stack()
                            return self.select_storage(path,
                                                       get_wallet_from_daemon)
                        except (UserCancelled, GoBack):
                            raise
                        except BaseException as e:
                            self.logger.exception('')
                            self.show_message(title=_('Error'), msg=repr(e))
                            raise UserCancelled()
                        if temp_storage.is_past_initial_decryption():
                            break
                        else:
                            raise UserCancelled()
                    else:
                        raise Exception('Unexpected encryption version')

        try:
            run_user_interaction_loop()
        finally:
            try:
                pw_e.clear()
            except RuntimeError:  # wrapped C/C++ object has been deleted.
                pass  # happens when decrypting with hw device

        return temp_storage.path, (temp_storage
                                   if temp_storage.file_exists() else None)
Exemplo n.º 38
0
 def description(self):
     return _("Send and receive payment requests via email")
Exemplo n.º 39
0
 def get_toolbar_buttons(self):
     return QLabel(_("Filter:")), self.change_button, self.used_button
Exemplo n.º 40
0
    def create_menu(self, position):
        from electrum_dash.wallet import Multisig_Wallet
        is_multisig = isinstance(self.wallet, Multisig_Wallet)
        can_delete = self.wallet.can_delete_address()
        selected = self.selectedItems()
        multi_select = len(selected) > 1
        addrs = [item.text(1) for item in selected]
        if not addrs:
            return
        if not multi_select:
            item = self.itemAt(position)
            col = self.currentColumn()
            if not item:
                return
            addr = addrs[0]
            if not is_address(addr):
                item.setExpanded(not item.isExpanded())
                return

        menu = QMenu()
        if not multi_select:
            column_title = self.headerItem().text(col)
            copy_text = item.text(col)
            menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(copy_text))
            menu.addAction(_('Details'), lambda: self.parent.show_address(addr))
            if col in self.editable_columns:
                menu.addAction(_("Edit {}").format(column_title), lambda: self.editItem(item, col))
            menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr))
            if self.wallet.can_export():
                menu.addAction(_("Private key"), lambda: self.parent.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))
            if can_delete:
                menu.addAction(_("Remove from wallet"), lambda: self.parent.remove_address(addr))
            addr_URL = block_explorer_URL(self.config, 'addr', addr)
            if addr_URL:
                menu.addAction(_("View on block explorer"), lambda: webbrowser.open(addr_URL))

            if not self.wallet.is_frozen(addr):
                menu.addAction(_("Freeze"), lambda: self.parent.set_frozen_state([addr], True))
            else:
                menu.addAction(_("Unfreeze"), lambda: self.parent.set_frozen_state([addr], False))

        coins = self.wallet.get_utxos(addrs)
        if coins:
            menu.addAction(_("Spend from"), lambda: self.parent.spend_coins(coins))

        run_hook('receive_menu', menu, addrs, self.wallet)
        menu.exec_(self.viewport().mapToGlobal(position))
Exemplo n.º 41
0
 def format_date(self, d):
     return str(datetime.date(d.year, d.month, d.day)) if d else _('None')
Exemplo n.º 42
0
 def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> QVariant:
     # note: this method is performance-critical.
     # it is called a lot, and so must run extremely fast.
     assert index.isValid()
     col = index.column()
     tx_item = self.transactions.value_from_pos(index.row())
     tx_hash = tx_item['txid']
     conf = tx_item['confirmations']
     txpos = tx_item['txpos_in_block'] or 0
     height = tx_item['height']
     islock = tx_item['islock']
     try:
         status, status_str = self.tx_status_cache[tx_hash]
     except KeyError:
         tx_mined_info = self.tx_mined_info_from_tx_item(tx_item)
         status, status_str = self.parent.wallet.get_tx_status(
             tx_hash, tx_mined_info, islock)
     if role == Qt.UserRole:
         # for sorting
         d = {
             HistoryColumns.STATUS_ICON:
             # height breaks ties for unverified txns
             # txpos breaks ties for verified same block txns
             ((conf, -status, -height, -islock) if not conf and islock else
              (conf, -status, -height, -txpos)),
             HistoryColumns.STATUS_TEXT:
             status_str,
             HistoryColumns.DIP2:
             tx_item.get('dip2', ''),
             HistoryColumns.DESCRIPTION:
             tx_item['label'],
             HistoryColumns.COIN_VALUE:
             tx_item['value'].value,
             HistoryColumns.RUNNING_COIN_BALANCE:
             tx_item['balance'].value,
             HistoryColumns.FIAT_VALUE:
             tx_item['fiat_value'].value
             if 'fiat_value' in tx_item else None,
             HistoryColumns.FIAT_ACQ_PRICE:
             tx_item['acquisition_price'].value
             if 'acquisition_price' in tx_item else None,
             HistoryColumns.FIAT_CAP_GAINS:
             tx_item['capital_gain'].value
             if 'capital_gain' in tx_item else None,
             HistoryColumns.TXID:
             tx_hash,
         }
         return QVariant(d[col])
     if role not in (Qt.DisplayRole, Qt.EditRole):
         if col == HistoryColumns.STATUS_ICON and role == Qt.DecorationRole:
             return QVariant(read_QIcon(TX_ICONS[status]))
         elif col == HistoryColumns.STATUS_ICON and role == Qt.ToolTipRole:
             c = str(conf) + _(' confirmation' + ('s' if conf != 1 else ''))
             if conf < 6 and islock:
                 res = 'InstantSend, %s' % c
             else:
                 res = c
             return QVariant(res)
         elif col != HistoryColumns.DESCRIPTION and role == Qt.TextAlignmentRole:
             return QVariant(Qt.AlignRight | Qt.AlignVCenter)
         elif col != HistoryColumns.DESCRIPTION and role == Qt.FontRole:
             monospace_font = QFont(MONOSPACE_FONT)
             return QVariant(monospace_font)
         elif col == HistoryColumns.DESCRIPTION and role == Qt.DecorationRole \
                 and self.parent.wallet.invoices.paid.get(tx_hash):
             return QVariant(read_QIcon("seal"))
         elif col == HistoryColumns.DIP2 and role == Qt.ForegroundRole:
             dip2_brush = QBrush(QColor("#1c75bc"))
             return QVariant(dip2_brush)
         elif col in (HistoryColumns.DESCRIPTION, HistoryColumns.COIN_VALUE) \
                 and role == Qt.ForegroundRole and tx_item['value'].value < 0:
             red_brush = QBrush(QColor("#BC1E1E"))
             return QVariant(red_brush)
         elif col == HistoryColumns.FIAT_VALUE and role == Qt.ForegroundRole \
                 and not tx_item.get('fiat_default') and tx_item.get('fiat_value') is not None:
             blue_brush = QBrush(QColor("#1E1EFF"))
             return QVariant(blue_brush)
         return QVariant()
     if col == HistoryColumns.STATUS_TEXT:
         return QVariant(status_str)
     elif col == HistoryColumns.DIP2:
         return QVariant(tx_item.get('dip2', ''))
     elif col == HistoryColumns.DESCRIPTION:
         return QVariant(tx_item['label'])
     elif col == HistoryColumns.COIN_VALUE:
         value = tx_item['value'].value
         v_str = self.parent.format_amount(value,
                                           is_diff=True,
                                           whitespaces=True)
         return QVariant(v_str)
     elif col == HistoryColumns.RUNNING_COIN_BALANCE:
         balance = tx_item['balance'].value
         balance_str = self.parent.format_amount(balance, whitespaces=True)
         return QVariant(balance_str)
     elif col == HistoryColumns.FIAT_VALUE and 'fiat_value' in tx_item:
         value_str = self.parent.fx.format_fiat(tx_item['fiat_value'].value)
         return QVariant(value_str)
     elif col == HistoryColumns.FIAT_ACQ_PRICE and \
             tx_item['value'].value < 0 and 'acquisition_price' in tx_item:
         # fixme: should use is_mine
         acq = tx_item['acquisition_price'].value
         return QVariant(self.parent.fx.format_fiat(acq))
     elif col == HistoryColumns.FIAT_CAP_GAINS and 'capital_gain' in tx_item:
         cg = tx_item['capital_gain'].value
         return QVariant(self.parent.fx.format_fiat(cg))
     elif col == HistoryColumns.TXID:
         return QVariant(tx_hash)
     return QVariant()
Exemplo n.º 43
0
 def on_n(n):
     n_label.setText(_('From {0} cosigners').format(n))
     cw.set_n(n)
     m_edit.setMaximum(n)
Exemplo n.º 44
0
 def done_processing_error(self, dialog, result):
     traceback.print_exception(*result, file=sys.stderr)
     dialog.show_error(_("Error synchronising labels") + ':\n' + str(result[:2]))
Exemplo n.º 45
0
 def done_processing_success(self, dialog, result):
     dialog.show_message(_("Your labels have been synchronised."))
Exemplo n.º 46
0
 def push(self, wallet):
     if not wallet.network: raise Exception(_('You are offline.'))
     return asyncio.run_coroutine_threadsafe(
         self.push_thread(wallet), wallet.network.asyncio_loop).result()
Exemplo n.º 47
0
 def settings_widget(self, window):
     return EnterButton(_('Settings'),
                        partial(self.settings_dialog, window))
Exemplo n.º 48
0
 def decrypt_message(self, sequence, message, password):
     raise UserFacingException(
         _('Encryption and decryption are not implemented by {}').format(
             self.device))
Exemplo n.º 49
0
from .seed_dialog import SeedLayout, KeysLayout
from .network_dialog import NetworkChoiceLayout
from .util import (MessageBoxMixin, Buttons, icon_path, ChoicesLayout, WWLabel,
                   InfoButton, char_width_in_lineedit, PasswordLineEdit)
from .password_dialog import PasswordLayout, PasswordLayoutForHW, PW_NEW
from .bip39_recovery_dialog import Bip39RecoveryDialog
from electrum_dash.plugin import run_hook, Plugins

if TYPE_CHECKING:
    from electrum_dash.simple_config import SimpleConfig
    from electrum_dash.wallet_db import WalletDB
    from . import ElectrumGui


MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\
                     + _("Leave this field empty if you want to disable encryption.")
MSG_HW_STORAGE_ENCRYPTION = _("Set wallet file encryption.") + '\n'\
                          + _("Your wallet file does not contain secrets, mostly just metadata. ") \
                          + _("It also contains your master public key that allows watching your addresses.") + '\n\n'\
                          + _("Note: If you enable this setting, you will need your hardware device to open your wallet.")
WIF_HELP_TEXT = (
    _('WIF keys are typed in Dash Electrum, based on script type.') + '\n\n' +
    _('A few examples') + ':\n' +
    'p2pkh:XERBBcaPf5D5...       \t-> XhGqfhnL...\n')
# note: full key is XERBBcaPf5D5oFXTEP7TdPWLem5ktc2Zr3AhhQhHVQaF49fDP6tN
MSG_PASSPHRASE_WARN_ISSUE4566 = _("Warning") + ": "\
                              + _("You have multiple consecutive whitespaces or leading/trailing "
                                  "whitespaces in your passphrase.") + " " \
                              + _("This is discouraged.") + " " \
                              + _("Due to a bug, old versions of Dash Electrum will NOT be creating the "
Exemplo n.º 50
0
 def on_m(m):
     m_label.setText(_('Require {0} signatures').format(m))
     cw.set_m(m)
     backup_warning_label.setVisible(cw.m != cw.n)
Exemplo n.º 51
0
 def receive_list_menu(self, menu, addr):
     window = get_parent_main_window(menu)
     menu.addAction(_("Send via e-mail"), lambda: self.send(window, addr))
Exemplo n.º 52
0
    def run_and_get_wallet(self):

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel(_('Wallet') + ':'))
        self.name_e = QLineEdit()
        hbox.addWidget(self.name_e)
        button = QPushButton(_('Choose...'))
        hbox.addWidget(button)
        vbox.addLayout(hbox)

        self.msg_label = QLabel('')
        vbox.addWidget(self.msg_label)
        hbox2 = QHBoxLayout()
        self.pw_e = QLineEdit('', self)
        self.pw_e.setFixedWidth(150)
        self.pw_e.setEchoMode(2)
        self.pw_label = QLabel(_('Password') + ':')
        hbox2.addWidget(self.pw_label)
        hbox2.addWidget(self.pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)
        self.set_layout(vbox, title=_('Electrum-DASH wallet'))

        wallet_folder = os.path.dirname(self.storage.path)

        def on_choose():
            path, __ = QFileDialog.getOpenFileName(self,
                                                   "Select your wallet file",
                                                   wallet_folder)
            if path:
                self.name_e.setText(path)

        def on_filename(filename):
            path = os.path.join(wallet_folder, filename)
            try:
                self.storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except IOError:
                self.storage = None
                self.next_button.setEnabled(False)
            if self.storage:
                if not self.storage.file_exists():
                    msg =_("This file does not exist.") + '\n' \
                          + _("Press 'Next' to create this wallet, or choose another file.")
                    pw = False
                elif self.storage.file_exists() and self.storage.is_encrypted(
                ):
                    msg = _("This file is encrypted.") + '\n' + _(
                        'Enter your password or choose another file.')
                    pw = True
                else:
                    msg = _("Press 'Next' to open this wallet.")
                    pw = False
            else:
                msg = _('Cannot read file')
                pw = False
            self.msg_label.setText(msg)
            if pw:
                self.pw_label.show()
                self.pw_e.show()
                self.pw_e.setFocus()
            else:
                self.pw_label.hide()
                self.pw_e.hide()

        button.clicked.connect(on_choose)
        self.name_e.textChanged.connect(on_filename)
        n = os.path.basename(self.storage.path)
        self.name_e.setText(n)

        while True:
            if self.storage.file_exists() and not self.storage.is_encrypted():
                break
            if self.loop.exec_() != 2:  # 2 = next
                return
            if not self.storage.file_exists():
                break
            if self.storage.file_exists() and self.storage.is_encrypted():
                password = self.pw_e.text()
                try:
                    self.storage.decrypt(password)
                    break
                except InvalidPassword as e:
                    QMessageBox.information(None, _('Error'), str(e))
                    continue
                except BaseException as e:
                    traceback.print_exc(file=sys.stdout)
                    QMessageBox.information(None, _('Error'), str(e))
                    return

        path = self.storage.path
        if self.storage.requires_split():
            self.hide()
            msg = _(
                "The wallet '%s' contains multiple accounts, which are no longer supported since Electrum-DASH 2.7.\n\n"
                "Do you want to split your wallet into multiple files?" % path)
            if not self.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = _('Your accounts have been moved to'
                    ) + ':\n' + file_list + '\n\n' + _(
                        'Do you want to delete the old file') + ':\n' + path
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        if self.storage.requires_upgrade():
            self.storage.upgrade()
            self.wallet = Wallet(self.storage)
            return self.wallet

        action = self.storage.get_action()
        if action and action != 'new':
            self.hide()
            msg = _("The file '%s' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?") % path
            if not self.question(msg):
                if self.question(_("Do you want to delete '%s'?") % path):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run
            self.run(action)
            return self.wallet

        self.wallet = Wallet(self.storage)
        return self.wallet
Exemplo n.º 53
0
 def decrypt_message(self, sequence, message, password):
     raise RuntimeError(_('Encryption and decryption are not implemented by {}').format(self.device))
Exemplo n.º 54
0
class InvoiceList(MyTreeView):

    class Columns(IntEnum):
        DATE = 0
        REQUESTOR = 1
        DESCRIPTION = 2
        AMOUNT = 3
        STATUS = 4

    headers = {
        Columns.DATE: _('Expires'),
        Columns.REQUESTOR: _('Requestor'),
        Columns.DESCRIPTION: _('Description'),
        Columns.AMOUNT: _('Amount'),
        Columns.STATUS: _('Status'),
    }
    filter_columns = [Columns.DATE, Columns.REQUESTOR, Columns.DESCRIPTION, Columns.AMOUNT]

    def __init__(self, parent):
        super().__init__(parent, self.create_menu,
                         stretch_column=self.Columns.DESCRIPTION,
                         editable_columns=[])
        self.setSortingEnabled(True)
        self.setColumnWidth(self.Columns.REQUESTOR, 200)
        self.setModel(QStandardItemModel(self))
        self.update()

    def update(self):
        inv_list = self.parent.invoices.unpaid_invoices()
        self.model().clear()
        self.update_headers(self.__class__.headers)
        self.header().setSectionResizeMode(self.Columns.REQUESTOR, QHeaderView.Interactive)
        for idx, pr in enumerate(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')
            labels = [date_str, requestor, pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')]
            items = [QStandardItem(e) for e in labels]
            self.set_editability(items)
            items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status)))
            items[self.Columns.DATE].setData(key, role=Qt.UserRole)
            items[self.Columns.REQUESTOR].setFont(QFont(MONOSPACE_FONT))
            items[self.Columns.AMOUNT].setFont(QFont(MONOSPACE_FONT))
            self.model().insertRow(idx, items)
        self.selectionModel().select(self.model().index(0,0), QItemSelectionModel.SelectCurrent)
        if self.parent.isVisible():
            b = len(inv_list) > 0
            self.setVisible(b)
            self.parent.invoices_label.setVisible(b)

    def import_invoices(self):
        import_meta_gui(self.parent, _('invoices'), self.parent.invoices.import_file, self.update)

    def export_invoices(self):
        export_meta_gui(self.parent, _('invoices'), self.parent.invoices.export_file)

    def create_menu(self, position):
        idx = self.indexAt(position)
        item = self.model().itemFromIndex(idx)
        item_col0 = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE))
        if not item or not item_col0:
            return
        key = item_col0.data(Qt.UserRole)
        column = idx.column()
        column_title = self.model().horizontalHeaderItem(column).text()
        column_data = item.text()
        status = self.parent.invoices.get_status(key)
        menu = QMenu(self)
        if column_data:
            menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data))
        menu.addAction(_("Details"), lambda: self.parent.show_invoice(key))
        if status == PR_UNPAID:
            menu.addAction(_("Pay Now"), lambda: self.parent.do_pay_invoice(key))
        menu.addAction(_("Delete"), lambda: self.parent.delete_invoice(key))
        menu.exec_(self.viewport().mapToGlobal(position))
Exemplo n.º 55
0
 def __str__(self):
     header = _("Error connecting to {} server").format('Labels')
     reason = self.reason
     if isinstance(reason, BaseException):
         reason = repr(reason)
     return f"{header}: {reason}" if reason else header
Exemplo n.º 56
0
 def import_invoices(self):
     import_meta_gui(self.parent, _('invoices'), self.parent.invoices.import_file, self.update)
Exemplo n.º 57
0
    def request_safe_t_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)
            vbox.addWidget(gb)
            gb.setTitle(_("Select your seed length:"))
            bg = QButtonGroup()
            for i, count in enumerate([12, 18, 24]):
                rb = QRadioButton(gb)
                rb.setText(_("%d words") % 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:
                msg = _("Enter the master private key beginning with xprv:")
                def set_enabled():
                    from electrum_dash.keystore import is_xprv
                    wizard.next_button.setEnabled(is_xprv(clean_text(text)))
                text.textChanged.connect(set_enabled)
                next_enabled = False

            vbox.addWidget(QLabel(msg))
            vbox.addWidget(text)
            pin = QLineEdit()
            pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,9}')))
            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())
Exemplo n.º 58
0
 def export_invoices(self):
     export_meta_gui(self.parent, _('invoices'), self.parent.invoices.export_file)
Exemplo n.º 59
0
 def on_m(m):
     m_label.setText(_('Require {0} signatures').format(m))
     cw.set_m(m)
Exemplo n.º 60
0
    def derivation_and_script_type_gui_specific_dialog(
        self,
        *,
        title: str,
        message1: str,
        choices: List[Tuple[str, str, str]],
        hide_choices: bool = False,
        message2: str,
        test_text: Callable[[str], int],
        run_next,
        default_choice_idx: int = 0,
        get_account_xpub=None,
    ) -> Tuple[str, str]:
        vbox = QVBoxLayout()

        if get_account_xpub:
            button = QPushButton(_("Detect Existing Accounts"))

            def on_account_select(account):
                script_type = account["script_type"]
                if script_type == "p2pkh":
                    script_type = "standard"
                button_index = c_values.index(script_type)
                button = clayout.group.buttons()[button_index]
                button.setChecked(True)
                line.setText(account["derivation_path"])

            button.clicked.connect(lambda: Bip39RecoveryDialog(
                self, get_account_xpub, on_account_select))
            vbox.addWidget(button, alignment=Qt.AlignLeft)
            vbox.addWidget(QLabel(_("Or")))

        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        c_default_text = [x[2] for x in choices]

        def on_choice_click(clayout):
            idx = clayout.selected_index()
            line.setText(c_default_text[idx])

        clayout = ChoicesLayout(message1,
                                c_titles,
                                on_choice_click,
                                checked_index=default_choice_idx)
        if not hide_choices:
            vbox.addLayout(clayout.layout())

        vbox.addWidget(WWLabel(message2))

        line = QLineEdit()

        def on_text_change(text):
            self.next_button.setEnabled(test_text(text))

        line.textEdited.connect(on_text_change)
        on_choice_click(clayout)  # set default text for "line"
        vbox.addWidget(line)

        self.exec_layout(vbox, title)
        choice = c_values[clayout.selected_index()]
        return str(line.text()), choice