electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

qt.py (4293B)


      1 from functools import partial
      2 import zlib
      3 import json
      4 from io import BytesIO
      5 import sys
      6 import platform
      7 from typing import TYPE_CHECKING
      8 
      9 from PyQt5.QtWidgets import (QComboBox, QGridLayout, QLabel, QPushButton)
     10 
     11 from electrum.plugin import BasePlugin, hook
     12 from electrum.gui.qt.util import WaitingDialog, EnterButton, WindowModalDialog, read_QIcon
     13 from electrum.i18n import _
     14 from electrum.logging import get_logger
     15 
     16 if TYPE_CHECKING:
     17     from electrum.gui.qt.transaction_dialog import TxDialog
     18 
     19 
     20 _logger = get_logger(__name__)
     21 
     22 
     23 try:
     24     import amodem.audio
     25     import amodem.main
     26     import amodem.config
     27     _logger.info('Audio MODEM is available.')
     28     amodem.log.addHandler(amodem.logging.StreamHandler(sys.stderr))
     29     amodem.log.setLevel(amodem.logging.INFO)
     30 except ImportError:
     31     amodem = None
     32     _logger.info('Audio MODEM is not found.')
     33 
     34 
     35 class Plugin(BasePlugin):
     36 
     37     def __init__(self, parent, config, name):
     38         BasePlugin.__init__(self, parent, config, name)
     39         if self.is_available():
     40             self.modem_config = amodem.config.slowest()
     41             self.library_name = {
     42                 'Linux': 'libportaudio.so'
     43             }[platform.system()]
     44 
     45     def is_available(self):
     46         return amodem is not None
     47 
     48     def requires_settings(self):
     49         return True
     50 
     51     def settings_widget(self, window):
     52         return EnterButton(_('Settings'), partial(self.settings_dialog, window))
     53 
     54     def settings_dialog(self, window):
     55         d = WindowModalDialog(window, _("Audio Modem Settings"))
     56 
     57         layout = QGridLayout(d)
     58         layout.addWidget(QLabel(_('Bit rate [kbps]: ')), 0, 0)
     59 
     60         bitrates = list(sorted(amodem.config.bitrates.keys()))
     61 
     62         def _index_changed(index):
     63             bitrate = bitrates[index]
     64             self.modem_config = amodem.config.bitrates[bitrate]
     65 
     66         combo = QComboBox()
     67         combo.addItems([str(x) for x in bitrates])
     68         combo.currentIndexChanged.connect(_index_changed)
     69         layout.addWidget(combo, 0, 1)
     70 
     71         ok_button = QPushButton(_("OK"))
     72         ok_button.clicked.connect(d.accept)
     73         layout.addWidget(ok_button, 1, 1)
     74 
     75         return bool(d.exec_())
     76 
     77     @hook
     78     def transaction_dialog(self, dialog: 'TxDialog'):
     79         b = QPushButton()
     80         b.setIcon(read_QIcon("speaker.png"))
     81 
     82         def handler():
     83             blob = dialog.tx.serialize()
     84             self._send(parent=dialog, blob=blob)
     85         b.clicked.connect(handler)
     86         dialog.sharing_buttons.insert(-1, b)
     87 
     88     @hook
     89     def scan_text_edit(self, parent):
     90         parent.addButton('microphone.png', partial(self._recv, parent),
     91                          _("Read from microphone"))
     92 
     93     @hook
     94     def show_text_edit(self, parent):
     95         def handler():
     96             blob = str(parent.toPlainText())
     97             self._send(parent=parent, blob=blob)
     98         parent.addButton('speaker.png', handler, _("Send to speaker"))
     99 
    100     def _audio_interface(self):
    101         interface = amodem.audio.Interface(config=self.modem_config)
    102         return interface.load(self.library_name)
    103 
    104     def _send(self, parent, blob):
    105         def sender_thread():
    106             with self._audio_interface() as interface:
    107                 src = BytesIO(blob)
    108                 dst = interface.player()
    109                 amodem.main.send(config=self.modem_config, src=src, dst=dst)
    110 
    111         _logger.info(f'Sending: {repr(blob)}')
    112         blob = zlib.compress(blob.encode('ascii'))
    113 
    114         kbps = self.modem_config.modem_bps / 1e3
    115         msg = 'Sending to Audio MODEM ({0:.1f} kbps)...'.format(kbps)
    116         WaitingDialog(parent, msg, sender_thread)
    117 
    118     def _recv(self, parent):
    119         def receiver_thread():
    120             with self._audio_interface() as interface:
    121                 src = interface.recorder()
    122                 dst = BytesIO()
    123                 amodem.main.recv(config=self.modem_config, src=src, dst=dst)
    124                 return dst.getvalue()
    125 
    126         def on_finished(blob):
    127             if blob:
    128                 blob = zlib.decompress(blob).decode('ascii')
    129                 _logger.info(f'Received: {repr(blob)}')
    130                 parent.setText(blob)
    131 
    132         kbps = self.modem_config.modem_bps / 1e3
    133         msg = 'Receiving from Audio MODEM ({0:.1f} kbps)...'.format(kbps)
    134         WaitingDialog(parent, msg, receiver_thread, on_finished)