URI: 
       tMerge pull request #6965 from SomberNight/202101_custom_block_explorer - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit d4ab4b37b76d9b21fb679cbda906ada52c99e964
   DIR parent bc6f2926f4b11c7d4293c7c286f7aec1a9889e03
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sun, 24 Jan 2021 10:38:09 +0100
       
       Merge pull request #6965 from SomberNight/202101_custom_block_explorer
       
       qt block explorer: allow custom URL
       Diffstat:
         M electrum/gui/qt/settings_dialog.py  |      44 ++++++++++++++++++++++++++-----
         M electrum/util.py                    |      36 ++++++++++++++++++++++++++-----
       
       2 files changed, 68 insertions(+), 12 deletions(-)
       ---
   DIR diff --git a/electrum/gui/qt/settings_dialog.py b/electrum/gui/qt/settings_dialog.py
       t@@ -23,13 +23,14 @@
        # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        # SOFTWARE.
        
       +import ast
        from typing import Optional, TYPE_CHECKING
        
        from PyQt5.QtCore import Qt
        from PyQt5.QtWidgets import (QComboBox,  QTabWidget,
                                     QSpinBox,  QFileDialog, QCheckBox, QLabel,
                                     QVBoxLayout, QGridLayout, QLineEdit,
       -                             QPushButton, QWidget)
       +                             QPushButton, QWidget, QHBoxLayout)
        
        from electrum.i18n import _
        from electrum import util, coinchooser, paymentrequest
       t@@ -328,16 +329,45 @@ Use this if you want your local watchtower to keep running after you close your 
                tx_widgets.append((outrounding_cb, None))
        
                block_explorers = sorted(util.block_explorer_info().keys())
       +        BLOCK_EX_CUSTOM_ITEM = _("Custom URL")
       +        if BLOCK_EX_CUSTOM_ITEM in block_explorers:  # malicious translation?
       +            block_explorers.remove(BLOCK_EX_CUSTOM_ITEM)
       +        block_explorers.append(BLOCK_EX_CUSTOM_ITEM)
                msg = _('Choose which online block explorer to use for functions that open a web browser')
                block_ex_label = HelpLabel(_('Online Block Explorer') + ':', msg)
                block_ex_combo = QComboBox()
       +        block_ex_custom_e = QLineEdit(self.config.get('block_explorer_custom') or '')
                block_ex_combo.addItems(block_explorers)
       -        block_ex_combo.setCurrentIndex(block_ex_combo.findText(util.block_explorer(self.config)))
       -        def on_be(x):
       -            be_result = block_explorers[block_ex_combo.currentIndex()]
       -            self.config.set_key('block_explorer', be_result, True)
       -        block_ex_combo.currentIndexChanged.connect(on_be)
       -        tx_widgets.append((block_ex_label, block_ex_combo))
       +        block_ex_combo.setCurrentIndex(
       +            block_ex_combo.findText(util.block_explorer(self.config) or BLOCK_EX_CUSTOM_ITEM))
       +        def showhide_block_ex_custom_e():
       +            block_ex_custom_e.setVisible(block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM)
       +        showhide_block_ex_custom_e()
       +        def on_be_combo(x):
       +            if block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM:
       +                on_be_edit()
       +            else:
       +                be_result = block_explorers[block_ex_combo.currentIndex()]
       +                self.config.set_key('block_explorer_custom', None, False)
       +                self.config.set_key('block_explorer', be_result, True)
       +            showhide_block_ex_custom_e()
       +        block_ex_combo.currentIndexChanged.connect(on_be_combo)
       +        def on_be_edit():
       +            val = block_ex_custom_e.text()
       +            try:
       +                val = ast.literal_eval(val)  # to also accept tuples
       +            except:
       +                pass
       +            self.config.set_key('block_explorer_custom', val)
       +        block_ex_custom_e.editingFinished.connect(on_be_edit)
       +        block_ex_hbox = QHBoxLayout()
       +        block_ex_hbox.setContentsMargins(0, 0, 0, 0)
       +        block_ex_hbox.setSpacing(0)
       +        block_ex_hbox.addWidget(block_ex_combo)
       +        block_ex_hbox.addWidget(block_ex_custom_e)
       +        block_ex_hbox_w = QWidget()
       +        block_ex_hbox_w.setLayout(block_ex_hbox)
       +        tx_widgets.append((block_ex_label, block_ex_hbox_w))
        
                # Fiat Currency
                hist_checkbox = QCheckBox()
   DIR diff --git a/electrum/util.py b/electrum/util.py
       t@@ -796,19 +796,43 @@ testnet_block_explorers = {
                               {'tx': 'tx/', 'addr': 'address/'}),
        }
        
       +_block_explorer_default_api_loc = {'tx': 'tx/', 'addr': 'address/'}
       +
       +
        def block_explorer_info():
            from . import constants
            return mainnet_block_explorers if not constants.net.TESTNET else testnet_block_explorers
        
       -def block_explorer(config: 'SimpleConfig') -> str:
       -    from . import constants
       +
       +def block_explorer(config: 'SimpleConfig') -> Optional[str]:
       +    """Returns name of selected block explorer,
       +    or None if a custom one (not among hardcoded ones) is configured.
       +    """
       +    if config.get('block_explorer_custom') is not None:
       +        return None
            default_ = 'Blockstream.info'
            be_key = config.get('block_explorer', default_)
       -    be = block_explorer_info().get(be_key)
       -    return be_key if be is not None else default_
       +    be_tuple = block_explorer_info().get(be_key)
       +    if be_tuple is None:
       +        be_key = default_
       +    assert isinstance(be_key, str), f"{be_key!r} should be str"
       +    return be_key
       +
        
        def block_explorer_tuple(config: 'SimpleConfig') -> Optional[Tuple[str, dict]]:
       -    return block_explorer_info().get(block_explorer(config))
       +    custom_be = config.get('block_explorer_custom')
       +    if custom_be:
       +        if isinstance(custom_be, str):
       +            return custom_be, _block_explorer_default_api_loc
       +        if isinstance(custom_be, (tuple, list)) and len(custom_be) == 2:
       +            return tuple(custom_be)
       +        _logger.warning(f"not using 'block_explorer_custom' from config. "
       +                        f"expected a str or a pair but got {custom_be!r}")
       +        return None
       +    else:
       +        # using one of the hardcoded block explorers
       +        return block_explorer_info().get(block_explorer(config))
       +
        
        def block_explorer_URL(config: 'SimpleConfig', kind: str, item: str) -> Optional[str]:
            be_tuple = block_explorer_tuple(config)
       t@@ -818,6 +842,8 @@ def block_explorer_URL(config: 'SimpleConfig', kind: str, item: str) -> Optional
            kind_str = explorer_dict.get(kind)
            if kind_str is None:
                return
       +    if explorer_url[-1] != "/":
       +        explorer_url += "/"
            url_parts = [explorer_url, kind_str, item]
            return ''.join(url_parts)