Ejemplo n.º 1
0
 def word_dialog(self, msg):
     dialog = WindowModalDialog(self.top_level_window(), "")
     hbox = QHBoxLayout(dialog)
     hbox.addWidget(QLabel(msg))
     text = QLineEdit()
     text.setMaximumWidth(100)
     text.returnPressed.connect(dialog.accept)
     hbox.addWidget(text)
     hbox.addStretch(1)
     dialog.exec_()  # Firmware cannot handle cancellation
     self.word = text.text()
     self.done.set()
Ejemplo n.º 2
0
 def pin_dialog(self, msg):
     # Needed e.g. when resetting a device
     self.clear_dialog()
     dialog = WindowModalDialog(self.top_level_window(), _("Enter PIN"))
     matrix = self.pin_matrix_widget_class()
     vbox = QVBoxLayout()
     vbox.addWidget(QLabel(msg))
     vbox.addWidget(matrix)
     vbox.addLayout(Buttons(CancelButton(dialog), OkButton(dialog)))
     dialog.setLayout(vbox)
     dialog.exec_()
     self.response = str(matrix.get_value())
     self.done.set()
Ejemplo n.º 3
0
    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>"
                               + _("Ready to encrypt for revealer {}")
                                    .format(self.versioned_seed.version+'_'+self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(WWLabel("<b>"+_("OR")+"</b> "+_("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit()
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel("<font color='red'>"
                                 + _("This version supports a maximum of {} characters.").format(self.MAX_PLAINTEXT_LEN)
                                 +"</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        return bool(d.exec_())
Ejemplo n.º 4
0
 def auth_dialog(self, window):
     d = WindowModalDialog(window, _("Authorization"))
     vbox = QVBoxLayout(d)
     pw = AmountEdit(None, is_int=True)
     msg = _('Please enter your Google Authenticator code')
     vbox.addWidget(QLabel(msg))
     grid = QGridLayout()
     grid.setSpacing(8)
     grid.addWidget(QLabel(_('Code')), 1, 0)
     grid.addWidget(pw, 1, 1)
     vbox.addLayout(grid)
     msg = _(
         'If you have lost your second factor, you need to restore your wallet from seed in order to request a new code.'
     )
     label = QLabel(msg)
     label.setWordWrap(1)
     vbox.addWidget(label)
     vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
     if not d.exec_():
         return
     return pw.get_amount()
Ejemplo n.º 5
0
    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11,11,1,1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(WWLabel("<b>"+_("Revealer Secret Backup Plugin")+"</b><br>"
                                    +_("To encrypt your backup, first we need to load some noise.")+"<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit()
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(WWLabel(_("or type an existing revealer code below and click 'next':")))
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(partial(self.cypherseed_dialog, window))
        vbox.addWidget(
            QLabel("<b>" + _("Warning") + "</b>: " + _("Each revealer should be used only once.")
                   +"<br>"+_("more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>")))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                traceback.print_exc(file=sys.stdout)
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())
Ejemplo n.º 6
0
    def settings_dialog(self, window):
        d = WindowModalDialog(window, _("Audio Modem Settings"))

        layout = QGridLayout(d)
        layout.addWidget(QLabel(_('Bit rate [kbps]: ')), 0, 0)

        bitrates = list(sorted(amodem.config.bitrates.keys()))

        def _index_changed(index):
            bitrate = bitrates[index]
            self.modem_config = amodem.config.bitrates[bitrate]

        combo = QComboBox()
        combo.addItems([str(x) for x in bitrates])
        combo.currentIndexChanged.connect(_index_changed)
        layout.addWidget(combo, 0, 1)

        ok_button = QPushButton(_("OK"))
        ok_button.clicked.connect(d.accept)
        layout.addWidget(ok_button, 1, 1)

        return bool(d.exec_())
Ejemplo n.º 7
0
    def settings_dialog(self, window):
        d = WindowModalDialog(window, _("Audio Modem Settings"))

        layout = QGridLayout(d)
        layout.addWidget(QLabel(_('Bit rate [kbps]: ')), 0, 0)

        bitrates = list(sorted(amodem.config.bitrates.keys()))

        def _index_changed(index):
            bitrate = bitrates[index]
            self.modem_config = amodem.config.bitrates[bitrate]

        combo = QComboBox()
        combo.addItems([str(x) for x in bitrates])
        combo.currentIndexChanged.connect(_index_changed)
        layout.addWidget(combo, 0, 1)

        ok_button = QPushButton(_("OK"))
        ok_button.clicked.connect(d.accept)
        layout.addWidget(ok_button, 1, 1)

        return bool(d.exec_())
Ejemplo n.º 8
0
 def settings_dialog(self, window):
     wallet = window.parent().wallet
     d = WindowModalDialog(window, _("Label Settings"))
     hbox = QHBoxLayout()
     hbox.addWidget(QLabel("Label sync options:"))
     upload = ThreadedButton("Force upload",
                             partial(self.push, wallet),
                             partial(self.done_processing_success, d),
                             partial(self.done_processing_error, d))
     download = ThreadedButton("Force download",
                               partial(self.pull, wallet, True),
                               partial(self.done_processing_success, d),
                               partial(self.done_processing_error, d))
     vbox = QVBoxLayout()
     vbox.addWidget(upload)
     vbox.addWidget(download)
     hbox.addLayout(vbox)
     vbox = QVBoxLayout(d)
     vbox.addLayout(hbox)
     vbox.addSpacing(20)
     vbox.addLayout(Buttons(OkButton(d)))
     return bool(d.exec_())
Ejemplo n.º 9
0
 def message_dialog(self, msg, on_cancel):
     # Called more than once during signing, to confirm output and fee
     self.clear_dialog()
     title = _('Please check your {} device').format(self.device)
     self.dialog = dialog = WindowModalDialog(self.top_level_window(),
                                              title)
     l = QLabel(msg)
     vbox = QVBoxLayout(dialog)
     vbox.addWidget(l)
     if on_cancel:
         dialog.rejected.connect(on_cancel)
         vbox.addLayout(Buttons(CancelButton(dialog)))
     dialog.show()
Ejemplo n.º 10
0
    def calibration_dialog(self, window):
        d = WindowModalDialog(window, _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(QLabel(''.join(["<br/>", _("If you have an old printer, or want optimal precision"),"<br/>",
                                       _("print the calibration pdf and follow the instructions "), "<br/>","<br/>",
                                    ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)
Ejemplo n.º 11
0
    def calibration_dialog(self, window):
        d = WindowModalDialog(window, _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(QLabel(''.join(["<br/>", _("If you have an old printer, or want optimal precision"),"<br/>",
                                       _("print the calibration pdf and follow the instructions "), "<br/>","<br/>",
                                    ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)
Ejemplo n.º 12
0
    def settings_dialog(self, window):
        # Return a settings dialog.
        d = WindowModalDialog(window, _("Bitpost settings"))
        vbox = QVBoxLayout(d)

        d.setMinimumSize(500, 200)
        vbox.addStretch()
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))
        d.show()
Ejemplo n.º 13
0
    def show_settings_dialog(self, window, success):
        if not success:
            window.show_message(_('Server not reachable.'))
            return

        wallet = window.wallet
        d = WindowModalDialog(window, _("TrustedCoin Information"))
        d.setMinimumSize(500, 200)
        vbox = QVBoxLayout(d)
        hbox = QHBoxLayout()

        logo = QLabel()
        logo.setPixmap(QPixmap(icon_path("tc.jpeg")))
        msg = _('This wallet is protected by TrustedCoin\'s two-factor authentication.') + '<br/>' \
              + _(
            "For more information, visit") + " <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>"
        label = QLabel(msg)
        label.setOpenExternalLinks(1)

        hbox.addStretch(10)
        hbox.addWidget(logo)
        hbox.addStretch(10)
        hbox.addWidget(label)
        hbox.addStretch(10)

        vbox.addLayout(hbox)
        vbox.addStretch(10)

        msg = _(
            'TrustedCoin charges a small fee to co-sign transactions. The fee depends on how many prepaid transactions you buy. An extra output is added to your transaction every time you run out of prepaid transactions.') + '<br/>'
        label = QLabel(msg)
        label.setWordWrap(1)
        vbox.addWidget(label)

        vbox.addStretch(10)
        grid = QGridLayout()
        vbox.addLayout(grid)

        price_per_tx = wallet.price_per_tx
        n_prepay = wallet.num_prepay(self.config)
        i = 0
        for k, v in sorted(price_per_tx.items()):
            if k == 1:
                continue
            grid.addWidget(QLabel("Pay every %d transactions:" % k), i, 0)
            grid.addWidget(QLabel(window.format_amount(v / k) + ' ' + window.base_unit() + "/tx"), i, 1)
            b = QRadioButton()
            b.setChecked(k == n_prepay)
            b.clicked.connect(lambda b, k=k: self.config.set_key('trustedcoin_prepay', k, True))
            grid.addWidget(b, i, 2)
            i += 1

        n = wallet.billing_info.get('tx_remaining', 0)
        grid.addWidget(QLabel(_("Your wallet has {} prepaid transactions.").format(n)), i, 0)
        vbox.addLayout(Buttons(CloseButton(d)))
        d.exec_()
Ejemplo n.º 14
0
 def pin_dialog(self, msg, show_strength):
     # Needed e.g. when resetting a device
     self.clear_dialog()
     dialog = WindowModalDialog(self.top_level_window(), _("Enter PIN"))
     matrix = self.pin_matrix_widget_class(show_strength)
     vbox = QVBoxLayout()
     vbox.addWidget(QLabel(msg))
     vbox.addWidget(matrix)
     vbox.addLayout(Buttons(CancelButton(dialog), OkButton(dialog)))
     dialog.setLayout(vbox)
     dialog.exec_()
     self.response = str(matrix.get_value())
     self.done.set()
Ejemplo n.º 15
0
    def parse_dialog(self, ui_tracker):
        self.clear_dialog()
        self.dialog = dialog = WindowModalDialog(self.top_level_window(), _("Ledger Status"))
        label = QLabel('Parsing transaction data...')

        def update():
            label.setText('Parsing transaction data...\nTx: {}/{}\nInputs: {}/{}\nOutputs: {}/{}'.format(
                ui_tracker.values[0],
                ui_tracker.values[1],
                ui_tracker.values[2],
                ui_tracker.values[3],
                ui_tracker.values[4],
                ui_tracker.values[5]))
            if self.loop_ui:
                QTimer.singleShot(500, update)

        update()
        vbox = QVBoxLayout(dialog)
        vbox.addWidget(label)
        dialog.show()
Ejemplo n.º 16
0
    def pin_dialog(self, msg):
        # Needed e.g. when resetting a device
        from trezorlib.qt.pinmatrix import PinMatrixWidget

        self.clear_dialog()
        dialog = WindowModalDialog(self.top_level_window(), _("Enter PIN"))
        matrix = PinMatrixWidget()
        vbox = QVBoxLayout()
        vbox.addWidget(QLabel(msg))
        vbox.addWidget(matrix)
        vbox.addLayout(Buttons(CancelButton(dialog), OkButton(dialog)))
        dialog.setLayout(vbox)
        dialog.exec_()
        self.response = str(matrix.get_value())
        self.done.set()
Ejemplo n.º 17
0
 def passphrase_dialog(self, msg, confirm):
     # If confirm is true, require the user to enter the passphrase twice
     parent = self.top_level_window()
     d = WindowModalDialog(parent, _("Enter Passphrase"))
     if confirm:
         OK_button = OkButton(d)
         playout = PasswordLayout(msg=msg, kind=PW_PASSPHRASE, OK_button=OK_button)
         vbox = QVBoxLayout()
         vbox.addLayout(playout.layout())
         vbox.addLayout(Buttons(CancelButton(d), OK_button))
         d.setLayout(vbox)
         passphrase = playout.new_password() if d.exec_() else None
     else:
         pw = PasswordLineEdit()
         pw.setMinimumWidth(200)
         vbox = QVBoxLayout()
         vbox.addWidget(WWLabel(msg))
         vbox.addWidget(pw)
         vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
         d.setLayout(vbox)
         passphrase = pw.text() if d.exec_() else None
     self.passphrase = passphrase
     self.done.set()
Ejemplo n.º 18
0
 def passphrase_dialog(self, msg, confirm):
     # If confirm is true, require the user to enter the passphrase twice
     parent = self.top_level_window()
     d = WindowModalDialog(parent, _("Enter Passphrase"))
     if confirm:
         OK_button = OkButton(d)
         playout = PasswordLayout(msg=msg, kind=PW_PASSPHRASE, OK_button=OK_button)
         vbox = QVBoxLayout()
         vbox.addLayout(playout.layout())
         vbox.addLayout(Buttons(CancelButton(d), OK_button))
         d.setLayout(vbox)
         passphrase = playout.new_password() if d.exec_() else None
     else:
         pw = QLineEdit()
         pw.setEchoMode(2)
         pw.setMinimumWidth(200)
         vbox = QVBoxLayout()
         vbox.addWidget(WWLabel(msg))
         vbox.addWidget(pw)
         vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
         d.setLayout(vbox)
         passphrase = pw.text() if d.exec_() else None
     self.passphrase = passphrase
     self.done.set()
Ejemplo n.º 19
0
 def _name_multisig_account(self):
     dialog = WindowModalDialog(None, "Create Multisig Account")
     vbox = QVBoxLayout()
     label = QLabel(
         _("Enter a descriptive name for your multisig account.\nYou should later be able to use the name to uniquely identify this multisig account"
           ))
     hl = QHBoxLayout()
     hl.addWidget(label)
     name = QLineEdit()
     name.setMaxLength(30)
     name.resize(200, 40)
     he = QHBoxLayout()
     he.addWidget(name)
     okButton = OkButton(dialog)
     hlb = QHBoxLayout()
     hlb.addWidget(okButton)
     hlb.addStretch(2)
     vbox.addLayout(hl)
     vbox.addLayout(he)
     vbox.addLayout(hlb)
     dialog.setLayout(vbox)
     dialog.exec_()
     return name.text().strip()
Ejemplo n.º 20
0
    def settings_dialog(self, window):
        # hack to workaround a bug: https://github.com/spesmilo/electrum/commit/4d8fcded4b42fd673bbb61f85aa99dc329be28a4
        if self.closed:
            # if this plugin instance is supposed to be closed and we have a different newer instance available,
            # forward the call to the newer one.
            if self.parent.bwt and self.parent.bwt != self:
                self.parent.bwt.settings_dialog(window)
            return

        if not self.wallets:
            window.show_error(_('No hd wallets found.'))
            return

        d = WindowModalDialog(window, _('Connect to Bitcoin Core with bwt'))
        d.setMinimumWidth(570)
        vbox = QVBoxLayout(d)

        form = QFormLayout()
        form.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
        vbox.addLayout(form)

        form.addRow(title(_('Bitcoin Core settings')))

        url_e = input(self.bitcoind_url)
        form.addRow(_('RPC URL:'), url_e)

        cred_e = input(self.bitcoind_cred)
        cred_e.setPlaceholderText('<username>:<password>')
        form.addRow(_('RPC Auth:'), cred_e)
        form.addRow('', helptext(_('Leave blank to use the cookie.'), False))

        dir_e = input(self.bitcoind_dir)
        form.addRow(_('Directory:'), dir_e)
        form.addRow('', helptext(_('Used for reading the cookie file. Ignored if auth is set.'), False))

        wallet_e = input(self.bitcoind_wallet, 150)
        form.addRow(_('Wallet:'), wallet_e)
        form.addRow('', helptext(_('For use with multi-wallet. Leave blank to use the default wallet.'), False))


        form.addRow(title(_('Other settings')))

        rescan_c = QComboBox()
        rescan_c.addItems([ _('All history'), _('Since date'), _('None') ])
        rescan_c.setMaximumWidth(150)
        rescan_e = input(None, 150)
        rescan_e.setPlaceholderText('yyyy-mm-dd')
        apply_rescan(self.rescan_since, rescan_c, rescan_e)
        rescan_c.currentIndexChanged.connect(lambda i: rescan_e.setVisible(i == 1))
        rescan_l = QHBoxLayout()
        rescan_l.addWidget(rescan_c)
        rescan_l.addWidget(rescan_e)
        form.addRow(_('Scan:'), rescan_l)
        form.addRow('', helptext(_('Set to the wallet creation date to reduce scanning time.'), False))

        custom_opt_e = input(self.custom_opt)
        custom_opt_e.setPlaceholderText('e.g. --gap-limit 50 --poll-interval 1')
        form.addRow('Options', custom_opt_e)
        form.addRow('', helptext(_('Additional custom options. Optional.'), False))

        verbose_c = QComboBox()
        verbose_c.addItems([ _('info'), _('debug'), _('trace') ])
        verbose_c.setCurrentIndex(self.verbose)
        verbose_c.setMaximumWidth(150)
        form.addRow('Log level:', verbose_c)

        log_t = QTextEdit()
        log_t.setReadOnly(True)
        log_t.setFixedHeight(80)
        log_t.setStyleSheet('QTextEdit { color: #888; font-size: 0.9em }')
        log_t.setWordWrapMode(QTextOption.WrapAnywhere)
        sp = log_t.sizePolicy()
        sp.setRetainSizeWhenHidden(True)
        log_t.setSizePolicy(sp)
        log_t.hide()
        form.addRow(log_t)

        self.log_signal.connect(partial(show_log, log_t))

        def save_config_and_run():
            self.enabled = True
            self.bitcoind_url = str(url_e.text())
            self.bitcoind_dir = str(dir_e.text())
            self.bitcoind_cred = str(cred_e.text())
            self.bitcoind_wallet = str(wallet_e.text())
            self.rescan_since = get_rescan_value(rescan_c, rescan_e)
            self.custom_opt = str(custom_opt_e.text())
            self.verbose = verbose_c.currentIndex()

            self.config.set_key('bwt_enabled', self.enabled)
            self.config.set_key('bwt_bitcoind_url', self.bitcoind_url)
            self.config.set_key('bwt_bitcoind_dir', self.bitcoind_dir)
            self.config.set_key('bwt_bitcoind_cred', self.bitcoind_cred)
            self.config.set_key('bwt_bitcoind_wallet', self.bitcoind_wallet)
            self.config.set_key('bwt_rescan_since', self.rescan_since)
            self.config.set_key('bwt_custom_opt', self.custom_opt)
            self.config.set_key('bwt_verbose', self.verbose)

            log_t.clear()
            log_t.show()

            self.start()

            window.show_message(_('bwt is starting, check the logs for additional information. The bwt server will be available after Bitcoin Core completes rescanning, which may take awhile.'))
            log_t.ensureCursorVisible()

        save_b = QPushButton('Save && Connect')
        save_b.setDefault(True)
        save_b.clicked.connect(save_config_and_run)

        vbox.addLayout(Buttons(CloseButton(d), save_b))

        d.exec_()
        self.log_signal.disconnect()
Ejemplo n.º 21
0
class Plugin(RevealerPlugin):

    MAX_PLAINTEXT_LEN = 189  # chars

    def __init__(self, parent, config, name):
        RevealerPlugin.__init__(self, parent, config, name)
        self.base_dir = os.path.join(config.electrum_path(), 'revealer')

        if self.config.get('calibration_h') is None:
            self.config.set_key('calibration_h', 0)
        if self.config.get('calibration_v') is None:
            self.config.set_key('calibration_v', 0)

        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')

        self.f_size = QSize(1014*2, 642*2)
        self.abstand_h = 21
        self.abstand_v = 34
        self.calibration_noise = int('10' * 128)
        self.rawnoise = False
        make_dir(self.base_dir)

        self.extension = False

    @hook
    def create_status_bar(self, parent):
        b = StatusBarButton(read_QIcon('revealer.png'), "Revealer "+_("secret backup utility"),
                            partial(self.setup_dialog, parent))
        parent.addPermanentWidget(b)

    def requires_settings(self):
        return True

    def settings_widget(self, window):
        return EnterButton(_('Printer Calibration'), partial(self.calibration_dialog, window))

    def password_dialog(self, msg=None, parent=None):
        from electrum.gui.qt.password_dialog import PasswordDialog
        parent = parent or self
        d = PasswordDialog(parent, msg)
        return d.run()

    def get_seed(self):
        password = None
        if self.wallet.has_keystore_encryption():
            password = self.password_dialog(parent=self.d.parent())
            if not password:
                raise UserCancelled()

        keystore = self.wallet.get_keystore()
        if not keystore or not keystore.has_seed():
            return
        self.extension = bool(keystore.get_passphrase(password))
        return keystore.get_seed(password)

    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11,11,1,1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(WWLabel("<b>"+_("Revealer Secret Backup Plugin")+"</b><br>"
                                    +_("To encrypt your backup, first we need to load some noise.")+"<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit()
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(WWLabel(_("or type an existing revealer code below and click 'next':")))
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(partial(self.cypherseed_dialog, window))
        vbox.addWidget(
            QLabel("<b>" + _("Warning") + "</b>: " + _("Each revealer should be used only once.")
                   +"<br>"+_("more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>")))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                traceback.print_exc(file=sys.stdout)
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())

    def get_noise(self):
        text = self.load_noise.text()
        return ''.join(text.split()).lower()

    def on_edit(self):
        txt = self.get_noise()
        versioned_seed = self.get_versioned_seed_from_user_input(txt)
        if versioned_seed:
            self.versioned_seed = versioned_seed
        self.user_input = bool(versioned_seed)
        self.next_button.setEnabled(bool(versioned_seed))

    def make_digital(self, dialog):
        self.make_rawnoise(True)
        self.bdone(dialog)
        self.d.close()

    def get_path_to_revealer_file(self, ext: str= '') -> str:
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        filename = self.filename_prefix + version + "_" + code_id + ext
        path = os.path.join(self.base_dir, filename)
        return os.path.normcase(os.path.abspath(path))

    def get_path_to_calibration_file(self):
        path = os.path.join(self.base_dir, 'calibration.pdf')
        return os.path.normcase(os.path.abspath(path))

    def bcrypt(self, dialog):
        self.rawnoise = False
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").format(self.was, version, code_id),
                                     "<b>", self.get_path_to_revealer_file(), "</b>", "<br/>",
                                     "<br/>", "<b>", _("Always check your backups.")]),
                            rich_text=True)
        dialog.close()

    def ext_warning(self, dialog):
        dialog.show_message(''.join(["<b>",_("Warning"), ": </b>",
                                     _("your seed extension will <b>not</b> be included in the encrypted backup.")]),
                            rich_text=True)
        dialog.close()

    def bdone(self, dialog):
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([_("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(version, code_id),
                                     "<br/>","<b>", self.get_path_to_revealer_file(), '</b>']),
                            rich_text=True)


    def customtxt_limits(self):
        txt = self.text.text()
        self.max_chars.setVisible(False)
        self.char_count.setText(f"({len(txt)}/{self.MAX_PLAINTEXT_LEN})")
        if len(txt)>0:
            self.ctext.setEnabled(True)
        if len(txt) > self.MAX_PLAINTEXT_LEN:
            self.text.setPlainText(txt[:self.MAX_PLAINTEXT_LEN])
            self.max_chars.setVisible(True)

    def t(self):
        self.txt = self.text.text()
        self.seed_img(is_seed=False)

    def warn_old_revealer(self):
        if self.versioned_seed.version == '0':
            link = "https://revealer.cc/revealer-warning-and-upgrade/"
            self.d.show_warning(("<b>{warning}: </b>{ver0}<br>"
                                 "{url}<br>"
                                 "{risk}")
                                .format(warning=_("Warning"),
                                        ver0=_("Revealers starting with 0 are not secure due to a vulnerability."),
                                        url=_("More info at: {}").format(f'<a href="{link}">{link}</a>'),
                                        risk=_("Proceed at your own risk.")),
                                rich_text=True)

    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>"
                               + _("Ready to encrypt for revealer {}")
                                    .format(self.versioned_seed.version+'_'+self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(WWLabel("<b>"+_("OR")+"</b> "+_("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit()
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel("<font color='red'>"
                                 + _("This version supports a maximum of {} characters.").format(self.MAX_PLAINTEXT_LEN)
                                 +"</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        return bool(d.exec_())

    def update_wallet_name(self, name):
        self.wallet_name = str(name)

    def seed_img(self, is_seed = True):

        if is_seed:
            try:
                cseed = self.get_seed()
            except UserCancelled:
                return
            except InvalidPassword as e:
                self.d.show_error(str(e))
                return
            if not cseed:
                self.d.show_message(_("This wallet has no seed"))
                return
            txt = cseed.upper()
        else:
            txt = self.txt.upper()

        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.white)
        painter = QPainter()
        painter.begin(bitmap)
        QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(__file__), 'SourceSansPro-Bold.otf') )
        if len(txt) < 102 :
            fontsize = 15
            linespace = 15
            max_letters = 17
            max_lines = 6
            max_words = 3
        else:
            fontsize = 12
            linespace = 10
            max_letters = 21
            max_lines = 9
            max_words = int(max_letters/4)

        font = QFont('Source Sans Pro', fontsize, QFont.Bold)
        font.setLetterSpacing(QFont.PercentageSpacing, 100)
        font.setPixelSize(fontsize)
        painter.setFont(font)
        seed_array = txt.split(' ')

        for n in range(max_lines):
            nwords = max_words
            temp_seed = seed_array[:nwords]
            while len(' '.join(map(str, temp_seed))) > max_letters:
               nwords = nwords - 1
               temp_seed = seed_array[:nwords]
            painter.drawText(QRect(0, linespace*n , self.SIZE[0], self.SIZE[1]), Qt.AlignHCenter, ' '.join(map(str, temp_seed)))
            del seed_array[:nwords]

        painter.end()
        img = bitmap.toImage()
        if (self.rawnoise == False):
            self.make_rawnoise()

        self.make_cypherseed(img, self.rawnoise, False, is_seed)
        return img

    def make_rawnoise(self, create_revealer=False):
        if not self.user_input:
            self.versioned_seed = self.gen_random_versioned_seed()
        assert self.versioned_seed
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)

        noise_map = self.get_noise_map(self.versioned_seed)
        for (x,y), pixel in noise_map.items():
            rawnoise.setPixel(x, y, pixel)

        self.rawnoise = rawnoise
        if create_revealer:
            self.make_revealer()

    def make_calnoise(self):
        random.seed(self.calibration_noise)
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)
        for x in range(w):
            for y in range(h):
                rawnoise.setPixel(x,y,random.randint(0, 1))
        self.calnoise = self.pixelcode_2x2(rawnoise)

    def make_revealer(self):
        revealer = self.pixelcode_2x2(self.rawnoise)
        revealer.invertPixels()
        revealer = QBitmap.fromImage(revealer)
        revealer = revealer.scaled(self.f_size, Qt.KeepAspectRatio)
        revealer = self.overlay_marks(revealer)

        self.filename_prefix = 'revealer_'
        revealer.save(self.get_path_to_revealer_file('.png'))
        self.toPdf(QImage(revealer))
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))

    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_prefix = 'custom_secret_'
            self.was = _('Custom secret')
        else:
            self.filename_prefix = self.wallet_name + '_seed_'
            self.was = self.wallet_name + ' ' + _('seed')
            if self.extension:
                self.ext_warning(self.c_dialog)


        if not calibration:
            self.toPdf(QImage(cypherseed))
            QDesktopServices.openUrl(QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))
            cypherseed.save(self.get_path_to_revealer_file('.png'))
            self.bcrypt(self.c_dialog)
        return cypherseed

    def calibration(self):
        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.black)
        self.make_calnoise()
        img = self.overlay_marks(self.calnoise.scaledToHeight(self.f_size.height()), False, True)
        self.calibration_pdf(img)
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.get_path_to_calibration_file()))
        return img

    def toPdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_revealer_file('.pdf'))
        printer.setPageMargins(0,0,0,0,6)
        painter = QPainter()
        painter.begin(printer)

        delta_h = round(image.width()/self.abstand_v)
        delta_v = round(image.height()/self.abstand_h)

        size_h = 2028+((int(self.calibration_h)*2028/(2028-(delta_h*2)+int(self.calibration_h)))/2)
        size_v = 1284+((int(self.calibration_v)*1284/(1284-(delta_v*2)+int(self.calibration_v)))/2)

        image =  image.scaled(size_h, size_v)

        painter.drawImage(553,533, image)
        wpath = QPainterPath()
        wpath.addRoundedRect(QRectF(553,533, size_h, size_v), 19, 19)
        painter.setPen(QPen(Qt.black, 1))
        painter.drawPath(wpath)
        painter.end()

    def calibration_pdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_calibration_file())
        printer.setPageMargins(0,0,0,0,6)

        painter = QPainter()
        painter.begin(printer)
        painter.drawImage(553,533, image)
        font = QFont('Source Sans Pro', 10, QFont.Bold)
        painter.setFont(font)
        painter.drawText(254,277, _("Calibration sheet"))
        font = QFont('Source Sans Pro', 7, QFont.Bold)
        painter.setFont(font)
        painter.drawText(600,2077, _("Instructions:"))
        font = QFont('Source Sans Pro', 7, QFont.Normal)
        painter.setFont(font)
        painter.drawText(700, 2177, _("1. Place this paper on a flat and well iluminated surface."))
        painter.drawText(700, 2277, _("2. Align your Revealer borderlines to the dashed lines on the top and left."))
        painter.drawText(700, 2377, _("3. Press slightly the Revealer against the paper and read the numbers that best "
                                      "match on the opposite sides. "))
        painter.drawText(700, 2477, _("4. Type the numbers in the software"))
        painter.end()

    def pixelcode_2x2(self, img):
        result = QImage(img.width()*2, img.height()*2, QImage.Format_ARGB32 )
        white = qRgba(255,255,255,0)
        black = qRgba(0,0,0,255)

        for x in range(img.width()):
            for y in range(img.height()):
                c = img.pixel(QPoint(x,y))
                colors = QColor(c).getRgbF()
                if colors[0]:
                    result.setPixel(x*2+1,y*2+1, black)
                    result.setPixel(x*2,y*2+1, white)
                    result.setPixel(x*2+1,y*2, white)
                    result.setPixel(x*2, y*2, black)

                else:
                    result.setPixel(x*2+1,y*2+1, white)
                    result.setPixel(x*2,y*2+1, black)
                    result.setPixel(x*2+1,y*2, black)
                    result.setPixel(x*2, y*2, white)
        return result

    def overlay_marks(self, img, is_cseed=False, calibration_sheet=False):
        border_color = Qt.white
        base_img = QImage(self.f_size.width(),self.f_size.height(), QImage.Format_ARGB32)
        base_img.fill(border_color)
        img = QImage(img)

        painter = QPainter()
        painter.begin(base_img)

        total_distance_h = round(base_img.width() / self.abstand_v)
        dist_v = round(total_distance_h) / 2
        dist_h = round(total_distance_h) / 2

        img = img.scaledToWidth(base_img.width() - (2 * (total_distance_h)))
        painter.drawImage(total_distance_h,
                          total_distance_h,
                          img)

        #frame around image
        pen = QPen(Qt.black, 2)
        painter.setPen(pen)

        #horz
        painter.drawLine(0, total_distance_h, base_img.width(), total_distance_h)
        painter.drawLine(0, base_img.height()-(total_distance_h), base_img.width(), base_img.height()-(total_distance_h))
        #vert
        painter.drawLine(total_distance_h, 0,  total_distance_h, base_img.height())
        painter.drawLine(base_img.width()-(total_distance_h), 0,  base_img.width()-(total_distance_h), base_img.height())

        #border around img
        border_thick = 6
        Rpath = QPainterPath()
        Rpath.addRect(QRectF((total_distance_h)+(border_thick/2),
                             (total_distance_h)+(border_thick/2),
                             base_img.width()-((total_distance_h)*2)-((border_thick)-1),
                             (base_img.height()-((total_distance_h))*2)-((border_thick)-1)))
        pen = QPen(Qt.black, border_thick)
        pen.setJoinStyle (Qt.MiterJoin)

        painter.setPen(pen)
        painter.drawPath(Rpath)

        Bpath = QPainterPath()
        Bpath.addRect(QRectF((total_distance_h), (total_distance_h),
                             base_img.width()-((total_distance_h)*2), (base_img.height()-((total_distance_h))*2)))
        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawPath(Bpath)

        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawLine(0, base_img.height()/2, total_distance_h, base_img.height()/2)
        painter.drawLine(base_img.width()/2, 0, base_img.width()/2, total_distance_h)

        painter.drawLine(base_img.width()-total_distance_h, base_img.height()/2, base_img.width(), base_img.height()/2)
        painter.drawLine(base_img.width()/2, base_img.height(), base_img.width()/2, base_img.height() - total_distance_h)

        #print code
        f_size = 37
        QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(__file__), 'DejaVuSansMono-Bold.ttf'))
        font = QFont("DejaVu Sans Mono", f_size-11, QFont.Bold)
        font.setPixelSize(35)
        painter.setFont(font)

        if not calibration_sheet:
            if is_cseed: #its a secret
                painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0,  dist_h, base_img.height())
                painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v))
                painter.drawLine(base_img.width()-(dist_h), 0,  base_img.width()-(dist_h), base_img.height())

                painter.drawImage(((total_distance_h))+11, ((total_distance_h))+11,
                                  QImage(icon_path('electrumb.png')).scaledToWidth(2.1*(total_distance_h), Qt.SmoothTransformation))

                painter.setPen(QPen(Qt.white, border_thick*8))
                painter.drawLine(base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2,
                                (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2,
                                base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2 - 77,
                                (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2)
                painter.setPen(QColor(0,0,0,255))
                painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick - 11,
                                       base_img.height()-total_distance_h - border_thick), Qt.AlignRight,
                                 self.versioned_seed.version + '_'+self.versioned_seed.checksum)
                painter.end()

            else: # revealer

                painter.setPen(QPen(border_color, 17))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0,  dist_h, base_img.height())
                painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v))
                painter.drawLine(base_img.width()-(dist_h), 0,  base_img.width()-(dist_h), base_img.height())

                painter.setPen(QPen(Qt.black, 2))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0,  dist_h, base_img.height())
                painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v))
                painter.drawLine(base_img.width()-(dist_h), 0,  base_img.width()-(dist_h), base_img.height())
                logo = QImage(icon_path('revealer_c.png')).scaledToWidth(1.3*(total_distance_h))
                painter.drawImage((total_distance_h)+ (border_thick), ((total_distance_h))+ (border_thick), logo, Qt.SmoothTransformation)

                #frame around logo
                painter.setPen(QPen(Qt.black, border_thick))
                painter.drawLine(total_distance_h+border_thick, total_distance_h+logo.height()+3*(border_thick/2),
                                 total_distance_h+logo.width()+border_thick, total_distance_h+logo.height()+3*(border_thick/2))
                painter.drawLine(logo.width()+total_distance_h+3*(border_thick/2), total_distance_h+(border_thick),
                                 total_distance_h+logo.width()+3*(border_thick/2), total_distance_h+logo.height()+(border_thick))

                #frame around code/qr
                qr_size = 179

                painter.drawLine((base_img.width()-((total_distance_h))-(border_thick/2)-2)-qr_size,
                                (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2,
                                 (base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size,
                                (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2)

                painter.drawLine((base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size,
                                (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2,
                                 base_img.width()/2 + (total_distance_h/2)-border_thick-(border_thick*8)/2-qr_size,
                                 ((base_img.height()-((total_distance_h)))-(border_thick/2)-2))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) - (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) - border_thick - qr_size,
                    (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2)

                painter.setPen(QColor(0,0,0,255))
                painter.drawText(QRect(((base_img.width()/2) +21)-qr_size, base_img.height()-107,
                                       base_img.width()-total_distance_h - border_thick -93,
                                       base_img.height()-total_distance_h - border_thick), Qt.AlignLeft, self.versioned_seed.get_ui_string_version_plus_seed())
                painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick -3 -qr_size,
                                       base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.versioned_seed.checksum)

                # draw qr code
                qr_qt = self.paintQR(self.versioned_seed.get_ui_string_version_plus_seed()
                                     + self.versioned_seed.checksum)
                target = QRectF(base_img.width()-65-qr_size,
                                base_img.height()-65-qr_size,
                                qr_size, qr_size )
                painter.drawImage(target, qr_qt)
                painter.setPen(QPen(Qt.black, 4))
                painter.drawLine(base_img.width()-65-qr_size,
                                base_img.height()-65-qr_size,
                                 base_img.width() - 65 - qr_size,
                                (base_img.height() - ((total_distance_h))) - ((border_thick * 8)) - (border_thick / 2) - 4
                                 )
                painter.drawLine(base_img.width()-65-qr_size,
                                base_img.height()-65-qr_size,
                                 base_img.width() - 65,
                                base_img.height()-65-qr_size
                                 )
                painter.end()

        else: # calibration only
            painter.end()
            cal_img = QImage(self.f_size.width() + 100, self.f_size.height() + 100,
                              QImage.Format_ARGB32)
            cal_img.fill(Qt.white)

            cal_painter = QPainter()
            cal_painter.begin(cal_img)
            cal_painter.drawImage(0,0, base_img)

            #black lines in the middle of border top left only
            cal_painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
            cal_painter.drawLine(0, dist_v, base_img.width(), dist_v)
            cal_painter.drawLine(dist_h, 0,  dist_h, base_img.height())

            pen = QPen(Qt.black, 2, Qt.DashDotDotLine)
            cal_painter.setPen(pen)
            n=15

            cal_painter.setFont(QFont("DejaVu Sans Mono", 21, QFont.Bold))
            for x in range(-n,n):
                #lines on bottom (vertical calibration)
                cal_painter.drawLine((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-13,
                                 x+2+base_img.height()-(dist_v),
                                 (((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)+13,
                                 x+2+base_img.height()-(dist_v))

                num_pos = 9
                if x > 9 : num_pos = 17
                if x < 0 : num_pos = 20
                if x < -9: num_pos = 27

                cal_painter.drawText((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-num_pos,
                                 50+base_img.height()-(dist_v),
                                  str(x))

                #lines on the right (horizontal calibrations)

                cal_painter.drawLine(x+2+(base_img.width()-(dist_h)),
                                 ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)-13,
                                 x+2+(base_img.width()-(dist_h)),
                                 ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)+13)


                cal_painter.drawText(30+(base_img.width()-(dist_h)),
                                ((base_img.height()/(2*n)) *(x))+ (base_img.height()/2)+13, str(x))

            cal_painter.end()
            base_img = cal_img

        return base_img

    def paintQR(self, data):
        if not data:
            return
        qr = qrcode.QRCode()
        qr.add_data(data)
        matrix = qr.get_matrix()
        k = len(matrix)
        border_color = Qt.white
        base_img = QImage(k * 5, k * 5, QImage.Format_ARGB32)
        base_img.fill(border_color)
        qrpainter = QPainter()
        qrpainter.begin(base_img)
        boxsize = 5
        size = k * boxsize
        left = (base_img.width() - size)/2
        top = (base_img.height() - size)/2
        qrpainter.setBrush(Qt.black)
        qrpainter.setPen(Qt.black)

        for r in range(k):
            for c in range(k):
                if matrix[r][c]:
                    qrpainter.drawRect(left+c*boxsize, top+r*boxsize, boxsize - 1, boxsize - 1)
        qrpainter.end()
        return base_img

    def calibration_dialog(self, window):
        d = WindowModalDialog(window, _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(QLabel(''.join(["<br/>", _("If you have an old printer, or want optimal precision"),"<br/>",
                                       _("print the calibration pdf and follow the instructions "), "<br/>","<br/>",
                                    ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)
Ejemplo n.º 22
0
def create_settings_window(small_window):
    window = get_parent_main_window(small_window)

    d = WindowModalDialog(window, _("Bitpost settings"))
    vbox = QVBoxLayout(d)

    d.setMinimumSize(500, 200)

    hbox_maxfee = QHBoxLayout()
    hbox_maxfee.addWidget(QLabel("Default max fee to use"))
    max_fees_e = QLineEdit()
    max_fees_e.setText(str(window.config.get('bitpost_max_fee')))
    hbox_maxfee.addWidget(max_fees_e)

    fee_combo = QComboBox()
    fee_combo_values = get_fee_units(window,
                                     window.config.get('bitpost_max_fee_unit'))

    fee_combo.addItems(fee_combo_values)
    hbox_maxfee.addWidget(fee_combo)

    help_button__max_fee = QPushButton("?")
    help_button__max_fee.clicked.connect(
        lambda: d.show_message(HelpTexts.max_fee))
    hbox_maxfee.addWidget(help_button__max_fee)

    vbox.addLayout(hbox_maxfee)

    empty_line = QHBoxLayout()
    empty_line.addWidget(QLabel(""))
    vbox.addLayout(empty_line)

    advanced_settings_title = QHBoxLayout()
    advanced_settings_title.addStretch()
    advanced_settings_title.addWidget(QLabel("<b>Notifications</b>"))
    advanced_settings_title.addStretch()
    vbox.addLayout(advanced_settings_title)

    telegram_reminder = QLabel(
        _("<b>Remember to start <a href='https:/t.me/bitpostbot'>@BitpostBot</a></b>"
          ))
    telegram_reminder.setVisible(True if window.config.get(
        'bitpost_notification_platform') == 'Telegram' else False)
    telegram_reminder.setOpenExternalLinks(True)

    platform_address = QHBoxLayout()
    platform_address.addWidget(QLabel("Platform"))
    platform_combo = QComboBox()
    fee_combo_values = ['None', 'Email', 'Twitter', 'Telegram']
    platform_combo.addItems(fee_combo_values)
    platform_combo.setCurrentText(
        window.config.get('bitpost_notification_platform'))
    platform_combo.currentTextChanged.connect(
        lambda: telegram_reminder.setVisible(True) if platform_combo.
        currentText() == 'Telegram' else telegram_reminder.setVisible(False))

    platform_address.addWidget(platform_combo)

    platform_address.addWidget(QLabel("Address/handle"))
    vbox.addLayout(platform_address)
    address_input = QLineEdit()
    address_input.setText(window.config.get('bitpost_notification_address',
                                            ''))
    platform_address.addWidget(address_input)

    vbox.addWidget(telegram_reminder)

    subscription_title = QHBoxLayout()
    subscription_title.addWidget(QLabel("Subscriptions"))
    subscriptions_help = QPushButton("?")
    subscriptions_help.clicked.connect(
        lambda: d.show_message(HelpTexts.subscriptions))
    subscription_title.addWidget(subscriptions_help)
    subscription_title.addStretch()
    vbox.addLayout(subscription_title)

    subscriptions = {
        subscription['name']
        for subscription in window.config.get(
            'bitpost_notification_subscriptions')
    }
    subscriptions1 = QVBoxLayout()
    overdue_checkbox = QCheckBox("Overdue")
    if 'overdue' in subscriptions:
        overdue_checkbox.setChecked(True)
    subscriptions1.addWidget(overdue_checkbox)

    mined_checkbox = QCheckBox("Mined")
    if 'mine' in subscriptions:
        mined_checkbox.setChecked(True)
    subscriptions1.addWidget(mined_checkbox)
    max_fee_reached_checkbox = QCheckBox("Maximum fee reached")
    if 'reached' in subscriptions:
        max_fee_reached_checkbox.setChecked(True)
    subscriptions1.addWidget(max_fee_reached_checkbox)
    vbox.addLayout(subscriptions1)

    reorg_checkbox = QCheckBox("Block reorganization")
    if 'orphaned_block' in subscriptions:
        reorg_checkbox.setChecked(True)
    subscriptions1.addWidget(reorg_checkbox)
    orphaned_checkbox = QCheckBox("Child transaction orphaned")
    if '' in subscriptions:
        orphaned_checkbox.setChecked(True)
    subscriptions1.addWidget(orphaned_checkbox)

    advanced_settings_title = QHBoxLayout()
    advanced_settings_title.addStretch()
    advanced_settings_title.addWidget(QLabel("<b>Advanced Settings</b>"))
    advanced_settings_title.addStretch()
    vbox.addLayout(advanced_settings_title)

    hbox_ntx = QHBoxLayout()
    hbox_ntx.addWidget(QLabel("Default number of Txs"))
    num_txs_e = QLineEdit()
    num_txs_e.setText(str(window.config.get('bitpost_num_txs')))
    hbox_ntx.addWidget(num_txs_e)
    help_button__num_txs = QPushButton("?")
    help_button__num_txs.clicked.connect(
        lambda: d.show_message(HelpTexts.num_txs))
    hbox_ntx.addWidget(help_button__num_txs)
    vbox.addLayout(hbox_ntx)

    broadcast_policy = QHBoxLayout()
    broadcast_policy.addWidget(QLabel("First broadcast policy"))

    broadcast_policy_combo = QComboBox()
    # 'Broadcast lowest fee transaction immediatly'
    broadcast_policy_options = [
        'Don\'t delay first broadcast', 'Allow delay of first broadcast'
    ]
    if window.config.get('bitpost_delay') == 1:
        broadcast_policy_options.reverse()

    broadcast_policy_combo.addItems(broadcast_policy_options)
    broadcast_policy.addWidget(broadcast_policy_combo)

    help_button__broadcast_policy = QPushButton("?")
    help_button__broadcast_policy.clicked.connect(
        lambda: d.show_message(HelpTexts.delay))
    broadcast_policy.addWidget(help_button__broadcast_policy)
    vbox.addLayout(broadcast_policy)

    vbox.addStretch()
    vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

    if not d.exec_():
        return

    window.config.set_key('bitpost_max_fee_unit', fee_combo.currentText())
    delay = 1 if broadcast_policy_combo.currentText(
    ) == 'Allow delay of first broadcast' else 0
    window.config.set_key('bitpost_delay', delay)
    platform_text = platform_combo.currentText()

    window.config.set_key('bitpost_notification_platform', platform_text)

    subscriptions = []
    if overdue_checkbox.isChecked():
        subscriptions.append({'name': 'overdue'})
    if mined_checkbox.isChecked():
        subscriptions.append({'name': 'mine'})
    if max_fee_reached_checkbox.isChecked():
        subscriptions.append({'name': 'reached'})
    if reorg_checkbox.isChecked():
        subscriptions.append({'name': 'orphaned_block'})
    if orphaned_checkbox.isChecked():
        pass  # TODO
    window.config.set_key('bitpost_notification_subscriptions', subscriptions)

    try:
        window.config.set_key('bitpost_max_fee', float(max_fees_e.text()))
    except:
        d.show_error('Invalid maximum fee, must be a number.')
        create_settings_window(small_window)

    try:
        window.config.set_key('bitpost_num_txs', int(num_txs_e.text()))
    except:
        d.show_error(
            'Invalid default number of transactions, must be an integer')
        create_settings_window(small_window)

    if not valid_address(platform_combo.currentText(), address_input.text()):
        d.show_error('Invalid handle/address for ' +
                     platform_combo.currentText())
        create_settings_window(small_window)

    window.config.set_key('bitpost_notification_address', address_input.text())
Ejemplo n.º 23
0
    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11, 11, 1, 1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(
            WWLabel(
                "<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" +
                _("To encrypt your backup, first we need to load some noise.")
                + "<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit()
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(
            WWLabel(
                _("or type an existing revealer code below and click 'next':"))
        )
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(
            partial(self.cypherseed_dialog, window))
        vbox.addWidget(
            QLabel("<b>" + _("Warning") + "</b>: " + _(
                "Each revealer should be used only once."
            ) + "<br>" + _(
                "more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>"
            )))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                self.logger.exception('')
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())
Ejemplo n.º 24
0
    def passphrase_dialog(self, msg, confirm):
        # If confirm is true, require the user to enter the passphrase twice
        parent = self.top_level_window()
        d = WindowModalDialog(parent, _('Enter Passphrase'))

        OK_button = OkButton(d, _('Enter Passphrase'))
        OnDevice_button = QPushButton(_('Enter Passphrase on Device'))

        new_pw = PasswordLineEdit()
        conf_pw = PasswordLineEdit()

        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)

        vbox.addWidget(label)

        grid.addWidget(QLabel(_('Passphrase:')), 0, 0)
        grid.addWidget(new_pw, 0, 1)

        if confirm:
            grid.addWidget(QLabel(_('Confirm Passphrase:')), 1, 0)
            grid.addWidget(conf_pw, 1, 1)

        vbox.addLayout(grid)

        def enable_OK():
            if not confirm:
                ok = True
            else:
                ok = new_pw.text() == conf_pw.text()
            OK_button.setEnabled(ok)

        new_pw.textChanged.connect(enable_OK)
        conf_pw.textChanged.connect(enable_OK)

        vbox.addWidget(OK_button)

        if self.passphrase_on_device:
            vbox.addWidget(OnDevice_button)

        d.setLayout(vbox)

        self.passphrase = None

        def ok_clicked():
            self.passphrase = new_pw.text()

        def on_device_clicked():
            self.passphrase = PASSPHRASE_ON_DEVICE

        OK_button.clicked.connect(ok_clicked)
        OnDevice_button.clicked.connect(on_device_clicked)
        OnDevice_button.clicked.connect(d.accept)

        d.exec_()
        self.done.set()
Ejemplo n.º 25
0
    def __init__(self, *, window: 'ElectrumWindow', txs, password, is_sweep):

        WindowModalDialog.__init__(self, window,
                                   _('BitPost Transactions Preview'))
        self.setMinimumSize(800, 600)
        self.main_window = window
        self.txs = txs
        self.password_required = self.main_window.wallet.has_keystore_encryption(
        ) and not is_sweep
        self.is_send = False
        vbox = QVBoxLayout()
        self.setLayout(vbox)
        lbox = QListWidget()

        items = []
        for tx in txs:
            inputs = tx.inputs()
            outputs = tx.outputs()
            fee = tx.get_fee()
            fiat = False

            text = "fee: {}".format(fee)
            if self.main_window.fx and self.main_window.fx.is_enabled():
                fiat = Exchange(self.main_window.fx)
                text += fiat.str_exchange(fee)

            text += "\t\tvbyte: {}\n".format(tx.estimated_size())

            tmp = "fee/vbyte: {}".format(round(fee / tx.estimated_size(), 2))
            if fiat:
                tmp += fiat.str_exchange(fee / tx.estimated_size())
            text += tmp
            if len(tmp) > 22:
                tab = "\t"
            else:
                tab = "\t\t"

            text += "{}total size: {}\n".format(tab, tx.estimated_total_size())

            text += "INPUTS:\n"
            for i in inputs:
                text += "{}:{} = {}".format(i.prevout.txid.hex(),
                                            i.prevout.out_idx, i.value_sats())
                if fiat:
                    text += fiat.str_exchange(i.value_sats())
                text += "\n"
            text += "OUTPUTS:\n"
            for o in outputs:
                text += "{} = {}".format(o.address, o.value)
                if fiat:
                    text += fiat.str_exchange(o.value)
                text += "\n"

            items.append(text)

        lbox.addItems(items)
        vbox.addWidget(lbox)

        self.send_button = QPushButton(_('Send'))
        self.send_button.clicked.connect(self.on_send)
        self.send_button.setDefault(True)

        self.pw_label = QLabel(_('Password'))
        self.pw_label.setVisible(self.password_required)
        self.pw = PasswordLineEdit(password)
        self.pw.setVisible(self.password_required)

        vbox.addLayout(
            Buttons(CancelButton(self), self.pw_label, self.pw,
                    self.send_button))
Ejemplo n.º 26
0
    def __init__(self, main_win, outputs):
        WindowModalDialog.__init__(self, main_win, title=_("New scheduled payment"),)
        vbox = QVBoxLayout(self)
        self.setMinimumSize(400, 150)
        minute_label = HelpLabel(_('minute:'), _('''
Defines a specific minute for an hour.
Allowed characters:
"*" any value
"," value list separator
"-" range of values
"/" step values
0-59 allowed values
'''))
        hour_label = HelpLabel(_('hour:'), _('''
Defines a specific hour during the day.
Allowed characters:
"*" any value
"," value list separator
"-" range of values
"/" step values
0-23 allowed values
'''))
        day_label = HelpLabel(_('day of month:'), _('''
Defines the day number for the month.
Allowed characters:
"*" any value
"," value list separator
"-" range of values
"/" step values
0-31 allowed values
'''))
        month_label = HelpLabel(_('month:'), _('''
Defines a month during the year.
Allowed characters:
"*" any value
"," value list separator
"-" range of values
"/" step values
1-12 allowed values
'''))
        week_label = HelpLabel(_('day of week:'), _('''
Specifies the day of the week.
Allowed characters:
"*" any value
"," value list separator
"-" range of values
"/" step values
0-6 allowed values
'''))
        self.minute_edit = QLineEdit('*')
        self.minute_edit.setFixedWidth(30)
        self.minute_edit.textChanged.connect(self.update_schedule_details)
        self.hour_edit = QLineEdit('*')
        self.hour_edit.setFixedWidth(30)
        self.hour_edit.textChanged.connect(self.update_schedule_details)
        self.day_edit = QLineEdit('*')
        self.day_edit.setFixedWidth(30)
        self.day_edit.textChanged.connect(self.update_schedule_details)
        self.month_edit = QLineEdit('*')
        self.month_edit.setFixedWidth(30)
        self.month_edit.textChanged.connect(self.update_schedule_details)
        self.week_edit = QLineEdit('*')
        self.week_edit.setFixedWidth(30)
        self.week_edit.textChanged.connect(self.update_schedule_details)
        grid = QGridLayout()
        grid.addWidget(minute_label, 0, 0, alignment=Qt.AlignCenter)
        grid.addWidget(hour_label, 0, 1, alignment=Qt.AlignCenter)
        grid.addWidget(day_label, 0, 2, alignment=Qt.AlignCenter)
        grid.addWidget(month_label, 0, 3, alignment=Qt.AlignCenter)
        grid.addWidget(week_label, 0, 4, alignment=Qt.AlignCenter)
        grid.addWidget(self.minute_edit, 1, 0, alignment=Qt.AlignCenter)
        grid.addWidget(self.hour_edit, 1, 1, alignment=Qt.AlignCenter)
        grid.addWidget(self.day_edit, 1, 2, alignment=Qt.AlignCenter)
        grid.addWidget(self.month_edit, 1, 3, alignment=Qt.AlignCenter)
        grid.addWidget(self.week_edit, 1, 4, alignment=Qt.AlignCenter)
        every_day = QPushButton('every day')
        every_day.clicked.connect(lambda: self.set_as_text('0 0 * * *'))
        every_week = QPushButton('every week')
        every_week.clicked.connect(lambda: self.set_as_text('0 0 * * 0'))
        every_month = QPushButton('every month')
        every_month.clicked.connect(lambda: self.set_as_text('0 0 1 * *'))
        every_year = QPushButton('every year')
        every_year.clicked.connect(lambda: self.set_as_text('0 0 1 1 *'))
        self.schedule_details = QLabel('')
        vbox.addLayout(grid)
        vbox.addLayout(Buttons(every_day, every_week, every_month, every_year))
        vbox.addWidget(self.schedule_details)
        vbox.addStretch()
        vbox.addLayout(Buttons(CloseButton(self), OkButton(self)))
        self.update_schedule_details()
Ejemplo n.º 27
0
    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(
            WWLabel("<b>" + _("Revealer Visual Cryptography Plugin") +
                    "</b><br><br>" +
                    _("Ready to encrypt for revealer {}").format(
                        self.versioned_seed.version + '_' +
                        self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(
            WWLabel("<b>" + _("OR") + "</b> " +
                    _("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit(config=self.config)
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel(
            "<font color='red'>" +
            _("This version supports a maximum of {} characters.").format(
                self.MAX_PLAINTEXT_LEN) + "</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        self.vbox.addWidget(
            QLabel("<br>" + "<b>" + _("Warning ") + "</b>: " + _(
                "each Revealer is a one-time-pad, use it for a single secret.")
                   ))
        return bool(d.exec_())
Ejemplo n.º 28
0
class Plugin(RevealerPlugin):

    MAX_PLAINTEXT_LEN = 189  # chars

    def __init__(self, parent, config, name):
        RevealerPlugin.__init__(self, parent, config, name)
        self.base_dir = os.path.join(config.electrum_path(), 'revealer')

        if self.config.get('calibration_h') is None:
            self.config.set_key('calibration_h', 0)
        if self.config.get('calibration_v') is None:
            self.config.set_key('calibration_v', 0)

        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')

        self.f_size = QSize(1014 * 2, 642 * 2)
        self.abstand_h = 21
        self.abstand_v = 34
        self.calibration_noise = int('10' * 128)
        self.rawnoise = False
        make_dir(self.base_dir)

        self.extension = False

    @hook
    def create_status_bar(self, parent):
        b = StatusBarButton(read_QIcon('revealer.png'),
                            "Revealer " + _("Visual Cryptography Plugin"),
                            partial(self.setup_dialog, parent))
        parent.addPermanentWidget(b)

    def requires_settings(self):
        return True

    def settings_widget(self, window):
        return EnterButton(_('Printer Calibration'),
                           partial(self.calibration_dialog, window))

    def password_dialog(self, msg=None, parent=None):
        from electrum.gui.qt.password_dialog import PasswordDialog
        parent = parent or self
        d = PasswordDialog(parent, msg)
        return d.run()

    def get_seed(self):
        password = None
        if self.wallet.has_keystore_encryption():
            password = self.password_dialog(parent=self.d.parent())
            if not password:
                raise UserCancelled()

        keystore = self.wallet.get_keystore()
        if not keystore or not keystore.has_seed():
            return
        self.extension = bool(keystore.get_passphrase(password))
        return keystore.get_seed(password)

    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11, 11, 1, 1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(
            WWLabel("<b>" + _("Revealer Visual Cryptography Plugin") +
                    "</b><br><br>" +
                    _("To encrypt a secret, first create or load noise.") +
                    "<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit(config=self.config)
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(
            WWLabel(
                _("or type an existing revealer code below and click 'next':"))
        )
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(
            partial(self.cypherseed_dialog, window))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                self.logger.exception('')
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())

    def get_noise(self):
        text = self.load_noise.text()
        return ''.join(text.split()).lower()

    def on_edit(self):
        txt = self.get_noise()
        versioned_seed = self.get_versioned_seed_from_user_input(txt)
        if versioned_seed:
            self.versioned_seed = versioned_seed
        self.user_input = bool(versioned_seed)
        self.next_button.setEnabled(bool(versioned_seed))

    def make_digital(self, dialog):
        self.make_rawnoise(True)
        self.bdone(dialog)
        self.d.close()

    def get_path_to_revealer_file(self, ext: str = '') -> str:
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        filename = self.filename_prefix + version + "_" + code_id + ext
        path = os.path.join(self.base_dir, filename)
        return os.path.normcase(os.path.abspath(path))

    def get_path_to_calibration_file(self):
        path = os.path.join(self.base_dir, 'calibration.pdf')
        return os.path.normcase(os.path.abspath(path))

    def bcrypt(self, dialog):
        self.rawnoise = False
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([
            _("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").
            format(self.was, version, code_id), "<b>",
            self.get_path_to_revealer_file(), "</b>", "<br/>", "<br/>", "<b>",
            _("Always check your backups.")
        ]),
                            rich_text=True)
        dialog.close()

    def ext_warning(self, dialog):
        dialog.show_message(''.join([
            "<b>",
            _("Warning"), ": </b>",
            _("your seed extension will <b>not</b> be included in the encrypted backup."
              )
        ]),
                            rich_text=True)
        dialog.close()

    def bdone(self, dialog):
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([
            _("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(
                version, code_id), "<br/>", "<b>",
            self.get_path_to_revealer_file(), '</b>'
        ]),
                            rich_text=True)

    def customtxt_limits(self):
        txt = self.text.text()
        self.max_chars.setVisible(False)
        self.char_count.setText(f"({len(txt)}/{self.MAX_PLAINTEXT_LEN})")
        if len(txt) > 0:
            self.ctext.setEnabled(True)
        if len(txt) > self.MAX_PLAINTEXT_LEN:
            self.text.setPlainText(txt[:self.MAX_PLAINTEXT_LEN])
            self.max_chars.setVisible(True)

    def t(self):
        self.txt = self.text.text()
        self.seed_img(is_seed=False)

    def warn_old_revealer(self):
        if self.versioned_seed.version == '0':
            link = "https://revealer.cc/revealer-warning-and-upgrade/"
            self.d.show_warning((
                "<b>{warning}: </b>{ver0}<br>"
                "{url}<br>"
                "{risk}"
            ).format(
                warning=_("Warning"),
                ver0=
                _("Revealers starting with 0 are not secure due to a vulnerability."
                  ),
                url=_("More info at: {}").format(
                    f'<a href="{link}">{link}</a>'),
                risk=_("Proceed at your own risk.")),
                                rich_text=True)

    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(
            WWLabel("<b>" + _("Revealer Visual Cryptography Plugin") +
                    "</b><br><br>" +
                    _("Ready to encrypt for revealer {}").format(
                        self.versioned_seed.version + '_' +
                        self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(
            WWLabel("<b>" + _("OR") + "</b> " +
                    _("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit(config=self.config)
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel(
            "<font color='red'>" +
            _("This version supports a maximum of {} characters.").format(
                self.MAX_PLAINTEXT_LEN) + "</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        self.vbox.addWidget(
            QLabel("<br>" + "<b>" + _("Warning ") + "</b>: " + _(
                "each Revealer is a one-time-pad, use it for a single secret.")
                   ))
        return bool(d.exec_())

    def update_wallet_name(self, name):
        self.wallet_name = str(name)

    def seed_img(self, is_seed=True):

        if is_seed:
            try:
                cseed = self.get_seed()
            except UserCancelled:
                return
            except InvalidPassword as e:
                self.d.show_error(str(e))
                return
            if not cseed:
                self.d.show_message(_("This wallet has no seed"))
                return
            txt = cseed.upper()
        else:
            txt = self.txt.upper()

        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.white)
        painter = QPainter()
        painter.begin(bitmap)
        QFontDatabase.addApplicationFont(
            os.path.join(os.path.dirname(__file__), 'SourceSansPro-Bold.otf'))
        if len(txt) < 102:
            fontsize = 15
            linespace = 15
            max_letters = 17
            max_lines = 6
            max_words = 3
        else:
            fontsize = 12
            linespace = 10
            max_letters = 21
            max_lines = 9
            max_words = int(max_letters / 4)

        font = QFont('Source Sans Pro', fontsize, QFont.Bold)
        font.setLetterSpacing(QFont.PercentageSpacing, 100)
        font.setPixelSize(fontsize)
        painter.setFont(font)
        seed_array = txt.split(' ')

        for n in range(max_lines):
            nwords = max_words
            temp_seed = seed_array[:nwords]
            while len(' '.join(map(str, temp_seed))) > max_letters:
                nwords = nwords - 1
                temp_seed = seed_array[:nwords]
            painter.drawText(
                QRect(0, linespace * n, self.SIZE[0], self.SIZE[1]),
                Qt.AlignHCenter, ' '.join(map(str, temp_seed)))
            del seed_array[:nwords]

        painter.end()
        img = bitmap.toImage()
        if (self.rawnoise == False):
            self.make_rawnoise()

        self.make_cypherseed(img, self.rawnoise, False, is_seed)
        return img

    def make_rawnoise(self, create_revealer=False):
        if not self.user_input:
            self.versioned_seed = self.gen_random_versioned_seed()
        assert self.versioned_seed
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)

        noise_map = self.get_noise_map(self.versioned_seed)
        for (x, y), pixel in noise_map.items():
            rawnoise.setPixel(x, y, pixel)

        self.rawnoise = rawnoise
        if create_revealer:
            self.make_revealer()

    def make_calnoise(self):
        random.seed(self.calibration_noise)
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)
        for x in range(w):
            for y in range(h):
                rawnoise.setPixel(x, y, random.randint(0, 1))
        self.calnoise = self.pixelcode_2x2(rawnoise)

    def make_revealer(self):
        revealer = self.pixelcode_2x2(self.rawnoise)
        revealer.invertPixels()
        revealer = QBitmap.fromImage(revealer)
        revealer = revealer.scaled(self.f_size, Qt.KeepAspectRatio)
        revealer = self.overlay_marks(revealer)

        self.filename_prefix = 'revealer_'
        revealer.save(self.get_path_to_revealer_file('.png'))
        self.toPdf(QImage(revealer))

    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_prefix = 'custom_secret_'
            self.was = _('Custom secret')
        else:
            self.filename_prefix = self.wallet_name + '_seed_'
            self.was = self.wallet_name + ' ' + _('seed')
            if self.extension:
                self.ext_warning(self.c_dialog)

        if not calibration:
            self.toPdf(QImage(cypherseed))
            cypherseed.save(self.get_path_to_revealer_file('.png'))
            self.bcrypt(self.c_dialog)
        return cypherseed

    def calibration(self):
        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.black)
        self.make_calnoise()
        img = self.overlay_marks(
            self.calnoise.scaledToHeight(self.f_size.height()), False, True)
        self.calibration_pdf(img)
        QDesktopServices.openUrl(
            QUrl.fromLocalFile(self.get_path_to_calibration_file()))
        return img

    def toPdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_revealer_file('.pdf'))
        printer.setPageMargins(0, 0, 0, 0, 6)
        painter = QPainter()
        painter.begin(printer)

        delta_h = round(image.width() / self.abstand_v)
        delta_v = round(image.height() / self.abstand_h)

        size_h = 2028 + ((int(self.calibration_h) * 2028 /
                          (2028 -
                           (delta_h * 2) + int(self.calibration_h))) / 2)
        size_v = 1284 + ((int(self.calibration_v) * 1284 /
                          (1284 -
                           (delta_v * 2) + int(self.calibration_v))) / 2)

        image = image.scaled(size_h, size_v)

        painter.drawImage(553, 533, image)
        wpath = QPainterPath()
        wpath.addRoundedRect(QRectF(553, 533, size_h, size_v), 19, 19)
        painter.setPen(QPen(Qt.black, 1))
        painter.drawPath(wpath)
        painter.end()

    def calibration_pdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_calibration_file())
        printer.setPageMargins(0, 0, 0, 0, 6)

        painter = QPainter()
        painter.begin(printer)
        painter.drawImage(553, 533, image)
        font = QFont('Source Sans Pro', 10, QFont.Bold)
        painter.setFont(font)
        painter.drawText(254, 277, _("Calibration sheet"))
        font = QFont('Source Sans Pro', 7, QFont.Bold)
        painter.setFont(font)
        painter.drawText(600, 2077, _("Instructions:"))
        font = QFont('Source Sans Pro', 7, QFont.Normal)
        painter.setFont(font)
        painter.drawText(
            700, 2177,
            _("1. Place this paper on a flat and well iluminated surface."))
        painter.drawText(
            700, 2277,
            _("2. Align your Revealer borderlines to the dashed lines on the top and left."
              ))
        painter.drawText(
            700, 2377,
            _("3. Press slightly the Revealer against the paper and read the numbers that best "
              "match on the opposite sides. "))
        painter.drawText(700, 2477, _("4. Type the numbers in the software"))
        painter.end()

    def pixelcode_2x2(self, img):
        result = QImage(img.width() * 2,
                        img.height() * 2, QImage.Format_ARGB32)
        white = qRgba(255, 255, 255, 0)
        black = qRgba(0, 0, 0, 255)

        for x in range(img.width()):
            for y in range(img.height()):
                c = img.pixel(QPoint(x, y))
                colors = QColor(c).getRgbF()
                if colors[0]:
                    result.setPixel(x * 2 + 1, y * 2 + 1, black)
                    result.setPixel(x * 2, y * 2 + 1, white)
                    result.setPixel(x * 2 + 1, y * 2, white)
                    result.setPixel(x * 2, y * 2, black)

                else:
                    result.setPixel(x * 2 + 1, y * 2 + 1, white)
                    result.setPixel(x * 2, y * 2 + 1, black)
                    result.setPixel(x * 2 + 1, y * 2, black)
                    result.setPixel(x * 2, y * 2, white)
        return result

    def overlay_marks(self, img, is_cseed=False, calibration_sheet=False):
        border_color = Qt.white
        base_img = QImage(self.f_size.width(), self.f_size.height(),
                          QImage.Format_ARGB32)
        base_img.fill(border_color)
        img = QImage(img)

        painter = QPainter()
        painter.begin(base_img)

        total_distance_h = round(base_img.width() / self.abstand_v)
        dist_v = round(total_distance_h) / 2
        dist_h = round(total_distance_h) / 2

        img = img.scaledToWidth(base_img.width() - (2 * (total_distance_h)))
        painter.drawImage(total_distance_h, total_distance_h, img)

        #frame around image
        pen = QPen(Qt.black, 2)
        painter.setPen(pen)

        #horz
        painter.drawLine(0, total_distance_h, base_img.width(),
                         total_distance_h)
        painter.drawLine(0,
                         base_img.height() - (total_distance_h),
                         base_img.width(),
                         base_img.height() - (total_distance_h))
        #vert
        painter.drawLine(total_distance_h, 0, total_distance_h,
                         base_img.height())
        painter.drawLine(base_img.width() - (total_distance_h), 0,
                         base_img.width() - (total_distance_h),
                         base_img.height())

        #border around img
        border_thick = 6
        Rpath = QPainterPath()
        Rpath.addRect(
            QRectF((total_distance_h) + (border_thick / 2),
                   (total_distance_h) + (border_thick / 2),
                   base_img.width() - ((total_distance_h) * 2) -
                   ((border_thick) - 1),
                   (base_img.height() -
                    ((total_distance_h)) * 2) - ((border_thick) - 1)))
        pen = QPen(Qt.black, border_thick)
        pen.setJoinStyle(Qt.MiterJoin)

        painter.setPen(pen)
        painter.drawPath(Rpath)

        Bpath = QPainterPath()
        Bpath.addRect(
            QRectF((total_distance_h), (total_distance_h),
                   base_img.width() - ((total_distance_h) * 2),
                   (base_img.height() - ((total_distance_h)) * 2)))
        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawPath(Bpath)

        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawLine(0,
                         base_img.height() / 2, total_distance_h,
                         base_img.height() / 2)
        painter.drawLine(base_img.width() / 2, 0,
                         base_img.width() / 2, total_distance_h)

        painter.drawLine(base_img.width() - total_distance_h,
                         base_img.height() / 2, base_img.width(),
                         base_img.height() / 2)
        painter.drawLine(base_img.width() / 2, base_img.height(),
                         base_img.width() / 2,
                         base_img.height() - total_distance_h)

        #print code
        f_size = 37
        QFontDatabase.addApplicationFont(
            os.path.join(os.path.dirname(__file__), 'DejaVuSansMono-Bold.ttf'))
        font = QFont("DejaVu Sans Mono", f_size - 11, QFont.Bold)
        font.setPixelSize(35)
        painter.setFont(font)

        if not calibration_sheet:
            if is_cseed:  #its a secret
                painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())

                painter.drawImage(
                    ((total_distance_h)) + 11, ((total_distance_h)) + 11,
                    QImage(icon_path('electrumb.png')).scaledToWidth(
                        2.1 * (total_distance_h), Qt.SmoothTransformation))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2 - 77,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2)
                painter.setPen(QColor(0, 0, 0, 255))
                painter.drawText(
                    QRect(
                        0,
                        base_img.height() - 107,
                        base_img.width() - total_distance_h - border_thick -
                        11,
                        base_img.height() - total_distance_h - border_thick),
                    Qt.AlignRight, self.versioned_seed.version + '_' +
                    self.versioned_seed.checksum)
                painter.end()

            else:  # revealer

                painter.setPen(QPen(border_color, 17))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())

                painter.setPen(QPen(Qt.black, 2))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())
                logo = QImage(icon_path('revealer_c.png')).scaledToWidth(
                    1.3 * (total_distance_h))
                painter.drawImage((total_distance_h) + (border_thick),
                                  ((total_distance_h)) + (border_thick), logo,
                                  Qt.SmoothTransformation)

                #frame around logo
                painter.setPen(QPen(Qt.black, border_thick))
                painter.drawLine(
                    total_distance_h + border_thick,
                    total_distance_h + logo.height() + 3 * (border_thick / 2),
                    total_distance_h + logo.width() + border_thick,
                    total_distance_h + logo.height() + 3 * (border_thick / 2))
                painter.drawLine(
                    logo.width() + total_distance_h + 3 * (border_thick / 2),
                    total_distance_h + (border_thick),
                    total_distance_h + logo.width() + 3 * (border_thick / 2),
                    total_distance_h + logo.height() + (border_thick))

                #frame around code/qr
                qr_size = 179

                painter.drawLine((base_img.width() - ((total_distance_h)) -
                                  (border_thick / 2) - 2) - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 2,
                                 (base_img.width() / 2 +
                                  (total_distance_h / 2) - border_thick -
                                  (border_thick * 8) / 2) - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 2)

                painter.drawLine(
                    (base_img.width() / 2 +
                     (total_distance_h / 2) - border_thick -
                     (border_thick * 8) / 2) - qr_size,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8)) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) -
                    border_thick - (border_thick * 8) / 2 - qr_size,
                    ((base_img.height() - ((total_distance_h))) -
                     (border_thick / 2) - 2))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) -
                    border_thick - qr_size,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2)

                painter.setPen(QColor(0, 0, 0, 255))
                painter.drawText(
                    QRect(((base_img.width() / 2) + 21) - qr_size,
                          base_img.height() - 107,
                          base_img.width() - total_distance_h - border_thick -
                          93,
                          base_img.height() - total_distance_h - border_thick),
                    Qt.AlignLeft,
                    self.versioned_seed.get_ui_string_version_plus_seed())
                painter.drawText(
                    QRect(
                        0,
                        base_img.height() - 107,
                        base_img.width() - total_distance_h - border_thick -
                        3 - qr_size,
                        base_img.height() - total_distance_h - border_thick),
                    Qt.AlignRight, self.versioned_seed.checksum)

                # draw qr code
                qr_qt = self.paintQR(
                    self.versioned_seed.get_ui_string_version_plus_seed() +
                    self.versioned_seed.checksum)
                target = QRectF(base_img.width() - 65 - qr_size,
                                base_img.height() - 65 - qr_size, qr_size,
                                qr_size)
                painter.drawImage(target, qr_qt)
                painter.setPen(QPen(Qt.black, 4))
                painter.drawLine(base_img.width() - 65 - qr_size,
                                 base_img.height() - 65 - qr_size,
                                 base_img.width() - 65 - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 4)
                painter.drawLine(base_img.width() - 65 - qr_size,
                                 base_img.height() - 65 - qr_size,
                                 base_img.width() - 65,
                                 base_img.height() - 65 - qr_size)
                painter.end()

        else:  # calibration only
            painter.end()
            cal_img = QImage(self.f_size.width() + 100,
                             self.f_size.height() + 100, QImage.Format_ARGB32)
            cal_img.fill(Qt.white)

            cal_painter = QPainter()
            cal_painter.begin(cal_img)
            cal_painter.drawImage(0, 0, base_img)

            #black lines in the middle of border top left only
            cal_painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
            cal_painter.drawLine(0, dist_v, base_img.width(), dist_v)
            cal_painter.drawLine(dist_h, 0, dist_h, base_img.height())

            pen = QPen(Qt.black, 2, Qt.DashDotDotLine)
            cal_painter.setPen(pen)
            n = 15

            cal_painter.setFont(QFont("DejaVu Sans Mono", 21, QFont.Bold))
            for x in range(-n, n):
                #lines on bottom (vertical calibration)
                cal_painter.drawLine((((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) - 13,
                                     x + 2 + base_img.height() - (dist_v),
                                     (((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) + 13,
                                     x + 2 + base_img.height() - (dist_v))

                num_pos = 9
                if x > 9: num_pos = 17
                if x < 0: num_pos = 20
                if x < -9: num_pos = 27

                cal_painter.drawText((((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) - num_pos,
                                     50 + base_img.height() - (dist_v), str(x))

                #lines on the right (horizontal calibrations)

                cal_painter.drawLine(
                    x + 2 + (base_img.width() - (dist_h)),
                    ((base_img.height() / (2 * n)) * (x)) +
                    (base_img.height() / n) + (base_img.height() / 2) - 13,
                    x + 2 + (base_img.width() - (dist_h)),
                    ((base_img.height() / (2 * n)) * (x)) +
                    (base_img.height() / n) + (base_img.height() / 2) + 13)

                cal_painter.drawText(30 + (base_img.width() - (dist_h)),
                                     ((base_img.height() / (2 * n)) *
                                      (x)) + (base_img.height() / 2) + 13,
                                     str(x))

            cal_painter.end()
            base_img = cal_img

        return base_img

    def paintQR(self, data):
        if not data:
            return
        qr = qrcode.QRCode()
        qr.add_data(data)
        matrix = qr.get_matrix()
        k = len(matrix)
        border_color = Qt.white
        base_img = QImage(k * 5, k * 5, QImage.Format_ARGB32)
        base_img.fill(border_color)
        qrpainter = QPainter()
        qrpainter.begin(base_img)
        boxsize = 5
        size = k * boxsize
        left = (base_img.width() - size) / 2
        top = (base_img.height() - size) / 2
        qrpainter.setBrush(Qt.black)
        qrpainter.setPen(Qt.black)

        for r in range(k):
            for c in range(k):
                if matrix[r][c]:
                    qrpainter.drawRect(left + c * boxsize, top + r * boxsize,
                                       boxsize - 1, boxsize - 1)
        qrpainter.end()
        return base_img

    def calibration_dialog(self, window):
        d = WindowModalDialog(window,
                              _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(
            QLabel(''.join([
                "<br/>",
                _("If you have an old printer, or want optimal precision"),
                "<br/>",
                _("print the calibration pdf and follow the instructions "),
                "<br/>",
                "<br/>",
            ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)