URI: 
       tMerge pull request #899 from Tafelpoot/qrcode_fix2 - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit bfbd6f0e9df5d05f9021e43168ef4e6fce95c60d
   DIR parent babead68b6e301ffbe56ca2707370b3895e174fa
  HTML Author: ThomasV <thomasv1@gmx.de>
       Date:   Fri, 24 Oct 2014 16:44:33 +0200
       
       Merge pull request #899 from Tafelpoot/qrcode_fix2
       
       QR code fixes
       Diffstat:
         M gui/qt/installwizard.py             |       2 +-
         M gui/qt/main_window.py               |      64 +++++++++++++++++++++----------
         M gui/qt/paytoedit.py                 |      15 ++++++++++-----
         M gui/qt/qrcodewidget.py              |       2 +-
         M gui/qt/qrtextedit.py                |      48 ++++++++++++++++++++++++-------
         M gui/qt/seed_dialog.py               |      11 ++++-------
         M gui/qt/util.py                      |       4 ++--
         M lib/qrscanner.py                    |      15 ++++++++++++---
       
       8 files changed, 111 insertions(+), 50 deletions(-)
       ---
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -128,7 +128,7 @@ class InstallWizard(QDialog):
            def enter_seed_dialog(self, msg, sid, func=None):
                if func is None:
                    func = self.is_any
       -        vbox, seed_e = seed_dialog.enter_seed_box(msg, sid)
       +        vbox, seed_e = seed_dialog.enter_seed_box(msg, self, sid)
                vbox.addStretch(1)
                hbox, button = ok_cancel_buttons2(self, _('Next'))
                vbox.addLayout(hbox)
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -45,7 +45,7 @@ from electrum import Imported_Wallet
        from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit
        from network_dialog import NetworkDialog
        from qrcodewidget import QRCodeWidget, QRDialog
       -from qrtextedit import QRTextEdit
       +from qrtextedit import ScanQRTextEdit, ShowQRTextEdit
        
        from decimal import Decimal
        
       t@@ -1828,15 +1828,36 @@ class ElectrumWindow(QMainWindow):
        
                main_layout = QGridLayout()
                mpk_dict = self.wallet.get_master_public_keys()
       -        i = 0
       -        for key, value in mpk_dict.items():
       -            main_layout.addWidget(QLabel(key), i, 0)
       -            mpk_text = QRTextEdit()
       -            mpk_text.setReadOnly(True)
       +        # filter out the empty keys (PendingAccount)
       +        mpk_dict = {acc:mpk for acc,mpk in mpk_dict.items() if mpk}
       +
       +        # only show the combobox in case multiple accounts are available
       +        if len(mpk_dict) > 1:
       +            main_layout.addWidget(QLabel(_("Select Account")), 0, 0)
       +
       +            combobox = QComboBox()
       +            for name in mpk_dict:
       +                combobox.addItem(name)
       +            combobox.setCurrentIndex(0)
       +            main_layout.addWidget(combobox, 1, 0)
       +
       +            account = unicode(combobox.currentText())
       +            mpk_text = ShowQRTextEdit(text=mpk_dict[account])
       +            mpk_text.setMaximumHeight(170)
       +            mpk_text.selectAll()    # for easy copying
       +            main_layout.addWidget(mpk_text, 2, 0)
       +
       +            def show_mpk(account):
       +                mpk = mpk_dict.get(unicode(account), "")
       +                mpk_text.setText(mpk)
       +
       +            combobox.currentIndexChanged[str].connect(lambda acc: show_mpk(acc))
       +        elif len(mpk_dict) == 1:
       +            mpk = mpk_dict.values()[0]
       +            mpk_text = ShowQRTextEdit(text=mpk)
                    mpk_text.setMaximumHeight(170)
       -            mpk_text.setText(value)
       -            main_layout.addWidget(mpk_text, i + 1, 0)
       -            i += 2
       +            mpk_text.selectAll()    # for easy copying
       +            main_layout.addWidget(mpk_text, 2, 0)
        
                vbox = QVBoxLayout()
                vbox.addLayout(main_layout)
       t@@ -1845,7 +1866,6 @@ class ElectrumWindow(QMainWindow):
                dialog.setLayout(vbox)
                dialog.exec_()
        
       -
            @protected
            def show_seed_dialog(self, password):
                if not self.wallet.has_seed():
       t@@ -1900,9 +1920,7 @@ class ElectrumWindow(QMainWindow):
                vbox = QVBoxLayout()
                vbox.addWidget( QLabel(_("Address") + ': ' + address))
                vbox.addWidget( QLabel(_("Public key") + ':'))
       -        keys = QRTextEdit()
       -        keys.setReadOnly(True)
       -        keys.setText('\n'.join(pubkey_list))
       +        keys = ShowQRTextEdit(text='\n'.join(pubkey_list))
                vbox.addWidget(keys)
                vbox.addLayout(close_button(d))
                d.setLayout(vbox)
       t@@ -1924,9 +1942,7 @@ class ElectrumWindow(QMainWindow):
                vbox = QVBoxLayout()
                vbox.addWidget( QLabel(_("Address") + ': ' + address))
                vbox.addWidget( QLabel(_("Private key") + ':'))
       -        keys = QRTextEdit()
       -        keys.setReadOnly(True)
       -        keys.setText('\n'.join(pk_list))
       +        keys = ShowQRTextEdit(text='\n'.join(pk_list))
                vbox.addWidget(keys)
                vbox.addLayout(close_button(d))
                d.setLayout(vbox)
       t@@ -2128,6 +2144,12 @@ class ElectrumWindow(QMainWindow):
        
            def read_tx_from_qrcode(self):
                from electrum import qrscanner
       +        if qrscanner.proc is None:
       +            try:
       +                qrscanner.init(self.config)
       +            except Exception, e:
       +                QMessageBox.warning(self, _('Error'), _(e), _('OK'))
       +                return
                try:
                    data = qrscanner.scan_qr(self.config)
                except BaseException, e:
       t@@ -2135,12 +2157,14 @@ class ElectrumWindow(QMainWindow):
                    return
                if not data:
                    return
       +        # if the user scanned a bitcoin URI
       +        if data.startswith("bitcoin:"):
       +            self.pay_from_URI(data)
       +            return
       +        # else if the user scanned an offline signed tx
                # transactions are binary, but qrcode seems to return utf8...
                z = data.decode('utf8')
       -        s = ''
       -        for b in z:
       -            s += chr(ord(b))
       -        data = s.encode('hex')
       +        data = ''.join(chr(ord(b)) for b in z).encode('hex')
                tx = self.tx_from_text(data)
                if not tx:
                    return
   DIR diff --git a/gui/qt/paytoedit.py b/gui/qt/paytoedit.py
       t@@ -18,7 +18,7 @@
        
        from PyQt4.QtCore import *
        from PyQt4.QtGui import *
       -from qrtextedit import QRTextEdit
       +from qrtextedit import ScanQRTextEdit
        
        import re
        from decimal import Decimal
       t@@ -30,11 +30,9 @@ RE_ALIAS = '(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>'
        frozen_style = "QWidget { background-color:none; border:none;}"
        normal_style = "QPlainTextEdit { }"
        
       -class PayToEdit(QRTextEdit):
       -
       +class PayToEdit(ScanQRTextEdit):
            def __init__(self, win):
       -        QRTextEdit.__init__(self)
       -        self.win = win
       +        super(PayToEdit,self).__init__(win=win)
                self.amount_edit = win.amount_e
                self.document().contentsChanged.connect(self.update_size)
                self.heightMin = 0
       t@@ -235,3 +233,10 @@ class PayToEdit(QRTextEdit):
                cr = self.cursorRect()
                cr.setWidth(self.c.popup().sizeHintForColumn(0) + self.c.popup().verticalScrollBar().sizeHint().width())
                self.c.complete(cr)
       +
       +
       +    def qr_input(self):
       +        data = super(PayToEdit,self).qr_input()
       +        if data.startswith("bitcoin:"):
       +            self.scan_f(data)
       +            # TODO: update fee
   DIR diff --git a/gui/qt/qrcodewidget.py b/gui/qt/qrcodewidget.py
       t@@ -114,7 +114,7 @@ class QRDialog(QDialog):
        
                    def copy_to_clipboard():
                        bmp.save_qrcode(qrw.qr, filename)
       -                self.parent().app.clipboard().setImage(QImage(filename))
       +                QApplication.clipboard().setImage(QImage(filename))
                        QMessageBox.information(None, _('Message'), _("QR code saved to clipboard"), _('OK'))
        
                    b = QPushButton(_("Copy"))
   DIR diff --git a/gui/qt/qrtextedit.py b/gui/qt/qrtextedit.py
       t@@ -3,14 +3,13 @@ from PyQt4.QtGui import *
        from PyQt4.QtCore import *
        
        class QRTextEdit(QPlainTextEdit):
       -
       +    """Abstract class for QR-code related TextEdits. Do not use directly."""
            def __init__(self, text=None):
       -        QPlainTextEdit.__init__(self, text)
       +        super(QRTextEdit, self).__init__(text)
                self.button = QToolButton(self)
                self.button.setIcon(QIcon(":icons/qrcode.png"))
                self.button.setStyleSheet("QToolButton { border: none; padding: 0px; }")
                self.button.setVisible(True)
       -        self.button.clicked.connect(lambda: self.qr_show() if self.isReadOnly() else self.qr_input())
                self.setText = self.setPlainText
        
            def resizeEvent(self, e):
       t@@ -21,13 +20,11 @@ class QRTextEdit(QPlainTextEdit):
                                 (self.rect().bottom() - frameWidth - sz.height()))
                return o
        
       -    def contextMenuEvent(self, e):
       -        m = self.createStandardContextMenu()
       -        if self.isReadOnly():
       -            m.addAction(_("Show as QR code"), self.qr_show)
       -        else:
       -            m.addAction(_("Read QR code"), self.qr_input)
       -        m.exec_(e.globalPos())
       +class ShowQRTextEdit(QRTextEdit):
       +    def __init__(self, text=None):
       +        super(ShowQRTextEdit, self).__init__(text)
       +        self.setReadOnly(1)
       +        self.button.clicked.connect(self.qr_show)
        
            def qr_show(self):
                from qrcodewidget import QRDialog
       t@@ -37,13 +34,42 @@ class QRTextEdit(QPlainTextEdit):
                    s = unicode(self.toPlainText())
                QRDialog(s).exec_()
        
       +    def contextMenuEvent(self, e):
       +        m = self.createStandardContextMenu()
       +        m.addAction(_("Show as QR code"), self.qr_show)
       +        m.exec_(e.globalPos())
       +
       +
       +class ScanQRTextEdit(QRTextEdit):
       +    def __init__(self, win, text=""):
       +        super(ScanQRTextEdit,self).__init__(text)
       +        self.setReadOnly(0)
       +        self.win = win
       +        assert win, "You must pass a window with access to the config to ScanQRTextEdit constructor."
       +        if win:
       +            assert hasattr(win,"config"), "You must pass a window with access to the config to ScanQRTextEdit constructor."
       +        self.button.clicked.connect(self.qr_input)
       +
       +
            def qr_input(self):
                from electrum import qrscanner
       +        if qrscanner.proc is None:
       +            try:
       +                qrscanner.init(self.win.config)
       +            except Exception, e:
       +                QMessageBox.warning(self, _('Error'), _(e), _('OK'))
       +                return
                try:
                    data = qrscanner.scan_qr(self.win.config)
                except BaseException, e:
       -            QMessageBox.warning(self.win, _('Error'), _(e), _('OK'))
       +            QMessageBox.warning(self, _('Error'), _(e), _('OK'))
                    return
                if type(data) != str:
                    return
                self.setText(data)
       +        return data
       +
       +    def contextMenuEvent(self, e):
       +        m = self.createStandardContextMenu()
       +        m.addAction(_("Read QR code"), self.qr_input)
       +        m.exec_(e.globalPos())
   DIR diff --git a/gui/qt/seed_dialog.py b/gui/qt/seed_dialog.py
       t@@ -23,7 +23,7 @@ from electrum.i18n import _
        from electrum import mnemonic
        from qrcodewidget import QRCodeWidget, QRDialog
        from util import close_button
       -from qrtextedit import QRTextEdit
       +from qrtextedit import ShowQRTextEdit, ScanQRTextEdit
        
        class SeedDialog(QDialog):
            def __init__(self, parent, seed, imported_keys):
       t@@ -72,15 +72,13 @@ def show_seed_box(seed, sid=None):
                       + _("If you ever need to recover your wallet from seed, you will need both this seed and your cold seed.") + " " \
        
            label1 = QLabel(msg+ ":")
       -    seed_text = QRTextEdit(seed)
       -    seed_text.setReadOnly(True)
       +    seed_text = ShowQRTextEdit(text=seed)
            seed_text.setMaximumHeight(130)
        
            label2 = QLabel(msg2)
            label2.setWordWrap(True)
        
            logo = QLabel()
       -
            logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56))
            logo.setMaximumWidth(60)
        
       t@@ -96,8 +94,7 @@ def show_seed_box(seed, sid=None):
            return vbox
        
        
       -def enter_seed_box(msg, sid=None):
       -
       +def enter_seed_box(msg, window, sid=None):
            vbox = QVBoxLayout()
            logo = QLabel()
            logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56))
       t@@ -106,7 +103,7 @@ def enter_seed_box(msg, sid=None):
            label = QLabel(msg)
            label.setWordWrap(True)
        
       -    seed_e = QRTextEdit()
       +    seed_e = ScanQRTextEdit(win=window)
            seed_e.setMaximumHeight(100)
            seed_e.setTabChangesFocus(True)
        
   DIR diff --git a/gui/qt/util.py b/gui/qt/util.py
       t@@ -137,7 +137,7 @@ def line_dialog(parent, title, label, ok_label, default=None):
                return unicode(txt.text())
        
        def text_dialog(parent, title, label, ok_label, default=None):
       -    from qrtextedit import QRTextEdit
       +    from qrtextedit import ScanQRTextEdit
            dialog = QDialog(parent)
            dialog.setMinimumWidth(500)
            dialog.setWindowTitle(title)
       t@@ -145,7 +145,7 @@ def text_dialog(parent, title, label, ok_label, default=None):
            l = QVBoxLayout()
            dialog.setLayout(l)
            l.addWidget(QLabel(label))
       -    txt = QRTextEdit()
       +    txt = ScanQRTextEdit(parent)
            if default:
                txt.setText(default)
            l.addWidget(txt)
   DIR diff --git a/lib/qrscanner.py b/lib/qrscanner.py
       t@@ -6,28 +6,37 @@ try:
        except ImportError:
            zbar = None
        
       +proc = None
        
       -def scan_qr(config):
       +def init(config):
            if not zbar:
                raise BaseException("\n".join([_("Cannot start QR scanner."),_("The zbar package is not available."),_("On Linux, try 'sudo apt-get install python-zbar'")]))
            device = config.get("video_device", "default")
            if device == 'default':
                device = ''
       +    global proc
            proc = zbar.Processor()
            proc.init(video_device=device)
       +
       +def scan_qr(self):
       +    if not zbar:
       +        raise BaseException("\n".join([_("Cannot start QR scanner."),_("The zbar package is not available."),_("On Linux, try 'sudo apt-get install python-zbar'")]))
       +    if proc is None:
       +        raise BaseException("Start proc first")
            proc.visible = True
            while True:
                try:
                    proc.process_one()
                except Exception:
                    # User closed the preview window
       -            return {}
       +            return ""
                for r in proc.results:
                    if str(r.type) != 'QRCODE':
                        continue
       +            # hiding the preview window stops the camera
       +            proc.visible = False
                    return r.data
        
       -
        def _find_system_cameras():
            device_root = "/sys/class/video4linux"
            devices = {} # Name -> device