URI: 
       tsettings_dialog.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       tsettings_dialog.py (25202B)
       ---
            1 #!/usr/bin/env python
            2 #
            3 # Electrum - lightweight Bitcoin client
            4 # Copyright (C) 2012 thomasv@gitorious
            5 #
            6 # Permission is hereby granted, free of charge, to any person
            7 # obtaining a copy of this software and associated documentation files
            8 # (the "Software"), to deal in the Software without restriction,
            9 # including without limitation the rights to use, copy, modify, merge,
           10 # publish, distribute, sublicense, and/or sell copies of the Software,
           11 # and to permit persons to whom the Software is furnished to do so,
           12 # subject to the following conditions:
           13 #
           14 # The above copyright notice and this permission notice shall be
           15 # included in all copies or substantial portions of the Software.
           16 #
           17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
           18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
           20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
           21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
           22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
           23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           24 # SOFTWARE.
           25 
           26 import ast
           27 from typing import Optional, TYPE_CHECKING
           28 
           29 from PyQt5.QtCore import Qt
           30 from PyQt5.QtWidgets import (QComboBox,  QTabWidget,
           31                              QSpinBox,  QFileDialog, QCheckBox, QLabel,
           32                              QVBoxLayout, QGridLayout, QLineEdit,
           33                              QPushButton, QWidget, QHBoxLayout)
           34 
           35 from electrum.i18n import _
           36 from electrum import util, coinchooser, paymentrequest
           37 from electrum.util import base_units_list
           38 
           39 from .util import (ColorScheme, WindowModalDialog, HelpLabel, Buttons,
           40                    CloseButton)
           41 
           42 from electrum.i18n import languages
           43 from electrum import qrscanner
           44 
           45 if TYPE_CHECKING:
           46     from electrum.simple_config import SimpleConfig
           47     from .main_window import ElectrumWindow
           48 
           49 
           50 class SettingsDialog(WindowModalDialog):
           51 
           52     def __init__(self, parent: 'ElectrumWindow', config: 'SimpleConfig'):
           53         WindowModalDialog.__init__(self, parent, _('Preferences'))
           54         self.config = config
           55         self.window = parent
           56         self.need_restart = False
           57         self.fx = self.window.fx
           58         self.wallet = self.window.wallet
           59         
           60         vbox = QVBoxLayout()
           61         tabs = QTabWidget()
           62         gui_widgets = []
           63         tx_widgets = []
           64         oa_widgets = []
           65 
           66         # language
           67         lang_help = _('Select which language is used in the GUI (after restart).')
           68         lang_label = HelpLabel(_('Language') + ':', lang_help)
           69         lang_combo = QComboBox()
           70         lang_combo.addItems(list(languages.values()))
           71         lang_keys = list(languages.keys())
           72         lang_cur_setting = self.config.get("language", '')
           73         try:
           74             index = lang_keys.index(lang_cur_setting)
           75         except ValueError:  # not in list
           76             index = 0
           77         lang_combo.setCurrentIndex(index)
           78         if not self.config.is_modifiable('language'):
           79             for w in [lang_combo, lang_label]: w.setEnabled(False)
           80         def on_lang(x):
           81             lang_request = list(languages.keys())[lang_combo.currentIndex()]
           82             if lang_request != self.config.get('language'):
           83                 self.config.set_key("language", lang_request, True)
           84                 self.need_restart = True
           85         lang_combo.currentIndexChanged.connect(on_lang)
           86         gui_widgets.append((lang_label, lang_combo))
           87 
           88         nz_help = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
           89         nz_label = HelpLabel(_('Zeros after decimal point') + ':', nz_help)
           90         nz = QSpinBox()
           91         nz.setMinimum(0)
           92         nz.setMaximum(self.config.decimal_point)
           93         nz.setValue(self.config.num_zeros)
           94         if not self.config.is_modifiable('num_zeros'):
           95             for w in [nz, nz_label]: w.setEnabled(False)
           96         def on_nz():
           97             value = nz.value()
           98             if self.config.num_zeros != value:
           99                 self.config.num_zeros = value
          100                 self.config.set_key('num_zeros', value, True)
          101                 self.window.history_list.update()
          102                 self.window.address_list.update()
          103         nz.valueChanged.connect(on_nz)
          104         gui_widgets.append((nz_label, nz))
          105 
          106         use_rbf = bool(self.config.get('use_rbf', True))
          107         use_rbf_cb = QCheckBox(_('Use Replace-By-Fee'))
          108         use_rbf_cb.setChecked(use_rbf)
          109         use_rbf_cb.setToolTip(
          110             _('If you check this box, your transactions will be marked as non-final,') + '\n' + \
          111             _('and you will have the possibility, while they are unconfirmed, to replace them with transactions that pay higher fees.') + '\n' + \
          112             _('Note that some merchants do not accept non-final transactions until they are confirmed.'))
          113         def on_use_rbf(x):
          114             self.config.set_key('use_rbf', bool(x))
          115             batch_rbf_cb.setEnabled(bool(x))
          116         use_rbf_cb.stateChanged.connect(on_use_rbf)
          117         tx_widgets.append((use_rbf_cb, None))
          118 
          119         batch_rbf_cb = QCheckBox(_('Batch RBF transactions'))
          120         batch_rbf_cb.setChecked(bool(self.config.get('batch_rbf', False)))
          121         batch_rbf_cb.setEnabled(use_rbf)
          122         batch_rbf_cb.setToolTip(
          123             _('If you check this box, your unconfirmed transactions will be consolidated into a single transaction.') + '\n' + \
          124             _('This will save fees.'))
          125         def on_batch_rbf(x):
          126             self.config.set_key('batch_rbf', bool(x))
          127         batch_rbf_cb.stateChanged.connect(on_batch_rbf)
          128         tx_widgets.append((batch_rbf_cb, None))
          129 
          130         # lightning
          131         lightning_widgets = []
          132 
          133         help_gossip = _("""If this option is enabled, Electrum will download the network
          134 channels graph and compute payment path locally, instead of using trampoline payments. """)
          135         gossip_cb = QCheckBox(_("Download network graph"))
          136         gossip_cb.setToolTip(help_gossip)
          137         gossip_cb.setChecked(bool(self.config.get('use_gossip', False)))
          138         def on_gossip_checked(x):
          139             use_gossip = bool(x)
          140             self.config.set_key('use_gossip', use_gossip)
          141             if use_gossip:
          142                 self.window.network.start_gossip()
          143             else:
          144                 self.window.network.run_from_another_thread(
          145                     self.window.network.stop_gossip())
          146             util.trigger_callback('ln_gossip_sync_progress')
          147             # FIXME: update all wallet windows
          148             util.trigger_callback('channels_updated', self.wallet)
          149         gossip_cb.stateChanged.connect(on_gossip_checked)
          150         lightning_widgets.append((gossip_cb, None))
          151 
          152         help_local_wt = _("""If this option is checked, Electrum will
          153 run a local watchtower and protect your channels even if your wallet is not
          154 open. For this to work, your computer needs to be online regularly.""")
          155         local_wt_cb = QCheckBox(_("Run a local watchtower"))
          156         local_wt_cb.setToolTip(help_local_wt)
          157         local_wt_cb.setChecked(bool(self.config.get('run_local_watchtower', False)))
          158         def on_local_wt_checked(x):
          159             self.config.set_key('run_local_watchtower', bool(x))
          160         local_wt_cb.stateChanged.connect(on_local_wt_checked)
          161         lightning_widgets.append((local_wt_cb, None))
          162 
          163         help_persist = _("""If this option is checked, Electrum will persist after
          164 you close all your wallet windows, and the Electrum icon will be visible in the taskbar.
          165 Use this if you want your local watchtower to keep running after you close your wallet.""")
          166         persist_cb = QCheckBox(_("Persist after all windows are closed"))
          167         persist_cb.setToolTip(help_persist)
          168         persist_cb.setChecked(bool(self.config.get('persist_daemon', False)))
          169         def on_persist_checked(x):
          170             self.config.set_key('persist_daemon', bool(x))
          171         persist_cb.stateChanged.connect(on_persist_checked)
          172         lightning_widgets.append((persist_cb, None))
          173 
          174         help_remote_wt = _("""To use a remote watchtower, enter the corresponding URL here""")
          175         remote_wt_cb = QCheckBox(_("Use a remote watchtower"))
          176         remote_wt_cb.setToolTip(help_remote_wt)
          177         remote_wt_cb.setChecked(bool(self.config.get('use_watchtower', False)))
          178         def on_remote_wt_checked(x):
          179             self.config.set_key('use_watchtower', bool(x))
          180             self.watchtower_url_e.setEnabled(bool(x))
          181         remote_wt_cb.stateChanged.connect(on_remote_wt_checked)
          182         watchtower_url = self.config.get('watchtower_url')
          183         self.watchtower_url_e = QLineEdit(watchtower_url)
          184         self.watchtower_url_e.setEnabled(self.config.get('use_watchtower', False))
          185         def on_wt_url():
          186             url = self.watchtower_url_e.text() or None
          187             watchtower_url = self.config.set_key('watchtower_url', url)
          188         self.watchtower_url_e.editingFinished.connect(on_wt_url)
          189         lightning_widgets.append((remote_wt_cb, self.watchtower_url_e))
          190 
          191         msg = _('OpenAlias record, used to receive coins and to sign payment requests.') + '\n\n'\
          192               + _('The following alias providers are available:') + '\n'\
          193               + '\n'.join(['https://cryptoname.co/', 'http://xmr.link']) + '\n\n'\
          194               + 'For more information, see https://openalias.org'
          195         alias_label = HelpLabel(_('OpenAlias') + ':', msg)
          196         alias = self.config.get('alias','')
          197         self.alias_e = QLineEdit(alias)
          198         self.set_alias_color()
          199         self.alias_e.editingFinished.connect(self.on_alias_edit)
          200         oa_widgets.append((alias_label, self.alias_e))
          201 
          202         # units
          203         units = base_units_list
          204         msg = (_('Base unit of your wallet.')
          205                + '\n1 BTC = 1000 mBTC. 1 mBTC = 1000 bits. 1 bit = 100 sat.\n'
          206                + _('This setting affects the Send tab, and all balance related fields.'))
          207         unit_label = HelpLabel(_('Base unit') + ':', msg)
          208         unit_combo = QComboBox()
          209         unit_combo.addItems(units)
          210         unit_combo.setCurrentIndex(units.index(self.window.base_unit()))
          211         def on_unit(x, nz):
          212             unit_result = units[unit_combo.currentIndex()]
          213             if self.window.base_unit() == unit_result:
          214                 return
          215             edits = self.window.amount_e, self.window.receive_amount_e
          216             amounts = [edit.get_amount() for edit in edits]
          217             self.config.set_base_unit(unit_result)
          218             nz.setMaximum(self.config.decimal_point)
          219             self.window.update_tabs()
          220             for edit, amount in zip(edits, amounts):
          221                 edit.setAmount(amount)
          222             self.window.update_status()
          223         unit_combo.currentIndexChanged.connect(lambda x: on_unit(x, nz))
          224         gui_widgets.append((unit_label, unit_combo))
          225 
          226         system_cameras = qrscanner._find_system_cameras()
          227         qr_combo = QComboBox()
          228         qr_combo.addItem("Default","default")
          229         for camera, device in system_cameras.items():
          230             qr_combo.addItem(camera, device)
          231         #combo.addItem("Manually specify a device", config.get("video_device"))
          232         index = qr_combo.findData(self.config.get("video_device"))
          233         qr_combo.setCurrentIndex(index)
          234         msg = _("Install the zbar package to enable this.")
          235         qr_label = HelpLabel(_('Video Device') + ':', msg)
          236         qr_combo.setEnabled(qrscanner.libzbar is not None)
          237         on_video_device = lambda x: self.config.set_key("video_device", qr_combo.itemData(x), True)
          238         qr_combo.currentIndexChanged.connect(on_video_device)
          239         gui_widgets.append((qr_label, qr_combo))
          240 
          241         colortheme_combo = QComboBox()
          242         colortheme_combo.addItem(_('Light'), 'default')
          243         colortheme_combo.addItem(_('Dark'), 'dark')
          244         index = colortheme_combo.findData(self.config.get('qt_gui_color_theme', 'default'))
          245         colortheme_combo.setCurrentIndex(index)
          246         colortheme_label = QLabel(_('Color theme') + ':')
          247         def on_colortheme(x):
          248             self.config.set_key('qt_gui_color_theme', colortheme_combo.itemData(x), True)
          249             self.need_restart = True
          250         colortheme_combo.currentIndexChanged.connect(on_colortheme)
          251         gui_widgets.append((colortheme_label, colortheme_combo))
          252 
          253         updatecheck_cb = QCheckBox(_("Automatically check for software updates"))
          254         updatecheck_cb.setChecked(bool(self.config.get('check_updates', False)))
          255         def on_set_updatecheck(v):
          256             self.config.set_key('check_updates', v == Qt.Checked, save=True)
          257         updatecheck_cb.stateChanged.connect(on_set_updatecheck)
          258         gui_widgets.append((updatecheck_cb, None))
          259 
          260         filelogging_cb = QCheckBox(_("Write logs to file"))
          261         filelogging_cb.setChecked(bool(self.config.get('log_to_file', False)))
          262         def on_set_filelogging(v):
          263             self.config.set_key('log_to_file', v == Qt.Checked, save=True)
          264             self.need_restart = True
          265         filelogging_cb.stateChanged.connect(on_set_filelogging)
          266         filelogging_cb.setToolTip(_('Debug logs can be persisted to disk. These are useful for troubleshooting.'))
          267         gui_widgets.append((filelogging_cb, None))
          268 
          269         preview_cb = QCheckBox(_('Advanced preview'))
          270         preview_cb.setChecked(bool(self.config.get('advanced_preview', False)))
          271         preview_cb.setToolTip(_("Open advanced transaction preview dialog when 'Pay' is clicked."))
          272         def on_preview(x):
          273             self.config.set_key('advanced_preview', x == Qt.Checked)
          274         preview_cb.stateChanged.connect(on_preview)
          275         tx_widgets.append((preview_cb, None))
          276 
          277         usechange_cb = QCheckBox(_('Use change addresses'))
          278         usechange_cb.setChecked(self.window.wallet.use_change)
          279         if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
          280         def on_usechange(x):
          281             usechange_result = x == Qt.Checked
          282             if self.window.wallet.use_change != usechange_result:
          283                 self.window.wallet.use_change = usechange_result
          284                 self.window.wallet.db.put('use_change', self.window.wallet.use_change)
          285                 multiple_cb.setEnabled(self.window.wallet.use_change)
          286         usechange_cb.stateChanged.connect(on_usechange)
          287         usechange_cb.setToolTip(_('Using change addresses makes it more difficult for other people to track your transactions.'))
          288         tx_widgets.append((usechange_cb, None))
          289 
          290         def on_multiple(x):
          291             multiple = x == Qt.Checked
          292             if self.wallet.multiple_change != multiple:
          293                 self.wallet.multiple_change = multiple
          294                 self.wallet.db.put('multiple_change', multiple)
          295         multiple_change = self.wallet.multiple_change
          296         multiple_cb = QCheckBox(_('Use multiple change addresses'))
          297         multiple_cb.setEnabled(self.wallet.use_change)
          298         multiple_cb.setToolTip('\n'.join([
          299             _('In some cases, use up to 3 change addresses in order to break '
          300               'up large coin amounts and obfuscate the recipient address.'),
          301             _('This may result in higher transactions fees.')
          302         ]))
          303         multiple_cb.setChecked(multiple_change)
          304         multiple_cb.stateChanged.connect(on_multiple)
          305         tx_widgets.append((multiple_cb, None))
          306 
          307         def fmt_docs(key, klass):
          308             lines = [ln.lstrip(" ") for ln in klass.__doc__.split("\n")]
          309             return '\n'.join([key, "", " ".join(lines)])
          310 
          311         choosers = sorted(coinchooser.COIN_CHOOSERS.keys())
          312         if len(choosers) > 1:
          313             chooser_name = coinchooser.get_name(self.config)
          314             msg = _('Choose coin (UTXO) selection method.  The following are available:\n\n')
          315             msg += '\n\n'.join(fmt_docs(*item) for item in coinchooser.COIN_CHOOSERS.items())
          316             chooser_label = HelpLabel(_('Coin selection') + ':', msg)
          317             chooser_combo = QComboBox()
          318             chooser_combo.addItems(choosers)
          319             i = choosers.index(chooser_name) if chooser_name in choosers else 0
          320             chooser_combo.setCurrentIndex(i)
          321             def on_chooser(x):
          322                 chooser_name = choosers[chooser_combo.currentIndex()]
          323                 self.config.set_key('coin_chooser', chooser_name)
          324             chooser_combo.currentIndexChanged.connect(on_chooser)
          325             tx_widgets.append((chooser_label, chooser_combo))
          326 
          327         def on_unconf(x):
          328             self.config.set_key('confirmed_only', bool(x))
          329         conf_only = bool(self.config.get('confirmed_only', False))
          330         unconf_cb = QCheckBox(_('Spend only confirmed coins'))
          331         unconf_cb.setToolTip(_('Spend only confirmed inputs.'))
          332         unconf_cb.setChecked(conf_only)
          333         unconf_cb.stateChanged.connect(on_unconf)
          334         tx_widgets.append((unconf_cb, None))
          335 
          336         def on_outrounding(x):
          337             self.config.set_key('coin_chooser_output_rounding', bool(x))
          338         enable_outrounding = bool(self.config.get('coin_chooser_output_rounding', True))
          339         outrounding_cb = QCheckBox(_('Enable output value rounding'))
          340         outrounding_cb.setToolTip(
          341             _('Set the value of the change output so that it has similar precision to the other outputs.') + '\n' +
          342             _('This might improve your privacy somewhat.') + '\n' +
          343             _('If enabled, at most 100 satoshis might be lost due to this, per transaction.'))
          344         outrounding_cb.setChecked(enable_outrounding)
          345         outrounding_cb.stateChanged.connect(on_outrounding)
          346         tx_widgets.append((outrounding_cb, None))
          347 
          348         block_explorers = sorted(util.block_explorer_info().keys())
          349         BLOCK_EX_CUSTOM_ITEM = _("Custom URL")
          350         if BLOCK_EX_CUSTOM_ITEM in block_explorers:  # malicious translation?
          351             block_explorers.remove(BLOCK_EX_CUSTOM_ITEM)
          352         block_explorers.append(BLOCK_EX_CUSTOM_ITEM)
          353         msg = _('Choose which online block explorer to use for functions that open a web browser')
          354         block_ex_label = HelpLabel(_('Online Block Explorer') + ':', msg)
          355         block_ex_combo = QComboBox()
          356         block_ex_custom_e = QLineEdit(self.config.get('block_explorer_custom') or '')
          357         block_ex_combo.addItems(block_explorers)
          358         block_ex_combo.setCurrentIndex(
          359             block_ex_combo.findText(util.block_explorer(self.config) or BLOCK_EX_CUSTOM_ITEM))
          360         def showhide_block_ex_custom_e():
          361             block_ex_custom_e.setVisible(block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM)
          362         showhide_block_ex_custom_e()
          363         def on_be_combo(x):
          364             if block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM:
          365                 on_be_edit()
          366             else:
          367                 be_result = block_explorers[block_ex_combo.currentIndex()]
          368                 self.config.set_key('block_explorer_custom', None, False)
          369                 self.config.set_key('block_explorer', be_result, True)
          370             showhide_block_ex_custom_e()
          371         block_ex_combo.currentIndexChanged.connect(on_be_combo)
          372         def on_be_edit():
          373             val = block_ex_custom_e.text()
          374             try:
          375                 val = ast.literal_eval(val)  # to also accept tuples
          376             except:
          377                 pass
          378             self.config.set_key('block_explorer_custom', val)
          379         block_ex_custom_e.editingFinished.connect(on_be_edit)
          380         block_ex_hbox = QHBoxLayout()
          381         block_ex_hbox.setContentsMargins(0, 0, 0, 0)
          382         block_ex_hbox.setSpacing(0)
          383         block_ex_hbox.addWidget(block_ex_combo)
          384         block_ex_hbox.addWidget(block_ex_custom_e)
          385         block_ex_hbox_w = QWidget()
          386         block_ex_hbox_w.setLayout(block_ex_hbox)
          387         tx_widgets.append((block_ex_label, block_ex_hbox_w))
          388 
          389         # Fiat Currency
          390         hist_checkbox = QCheckBox()
          391         hist_capgains_checkbox = QCheckBox()
          392         fiat_address_checkbox = QCheckBox()
          393         ccy_combo = QComboBox()
          394         ex_combo = QComboBox()
          395 
          396         def update_currencies():
          397             if not self.window.fx: return
          398             currencies = sorted(self.fx.get_currencies(self.fx.get_history_config()))
          399             ccy_combo.clear()
          400             ccy_combo.addItems([_('None')] + currencies)
          401             if self.fx.is_enabled():
          402                 ccy_combo.setCurrentIndex(ccy_combo.findText(self.fx.get_currency()))
          403 
          404         def update_history_cb():
          405             if not self.fx: return
          406             hist_checkbox.setChecked(self.fx.get_history_config())
          407             hist_checkbox.setEnabled(self.fx.is_enabled())
          408 
          409         def update_fiat_address_cb():
          410             if not self.fx: return
          411             fiat_address_checkbox.setChecked(self.fx.get_fiat_address_config())
          412 
          413         def update_history_capgains_cb():
          414             if not self.fx: return
          415             hist_capgains_checkbox.setChecked(self.fx.get_history_capital_gains_config())
          416             hist_capgains_checkbox.setEnabled(hist_checkbox.isChecked())
          417 
          418         def update_exchanges():
          419             if not self.fx: return
          420             b = self.fx.is_enabled()
          421             ex_combo.setEnabled(b)
          422             if b:
          423                 h = self.fx.get_history_config()
          424                 c = self.fx.get_currency()
          425                 exchanges = self.fx.get_exchanges_by_ccy(c, h)
          426             else:
          427                 exchanges = self.fx.get_exchanges_by_ccy('USD', False)
          428             ex_combo.blockSignals(True)
          429             ex_combo.clear()
          430             ex_combo.addItems(sorted(exchanges))
          431             ex_combo.setCurrentIndex(ex_combo.findText(self.fx.config_exchange()))
          432             ex_combo.blockSignals(False)
          433 
          434         def on_currency(hh):
          435             if not self.fx: return
          436             b = bool(ccy_combo.currentIndex())
          437             ccy = str(ccy_combo.currentText()) if b else None
          438             self.fx.set_enabled(b)
          439             if b and ccy != self.fx.ccy:
          440                 self.fx.set_currency(ccy)
          441             update_history_cb()
          442             update_exchanges()
          443             self.window.update_fiat()
          444 
          445         def on_exchange(idx):
          446             exchange = str(ex_combo.currentText())
          447             if self.fx and self.fx.is_enabled() and exchange and exchange != self.fx.exchange.name():
          448                 self.fx.set_exchange(exchange)
          449 
          450         def on_history(checked):
          451             if not self.fx: return
          452             self.fx.set_history_config(checked)
          453             update_exchanges()
          454             self.window.history_model.refresh('on_history')
          455             if self.fx.is_enabled() and checked:
          456                 self.fx.trigger_update()
          457             update_history_capgains_cb()
          458 
          459         def on_history_capgains(checked):
          460             if not self.fx: return
          461             self.fx.set_history_capital_gains_config(checked)
          462             self.window.history_model.refresh('on_history_capgains')
          463 
          464         def on_fiat_address(checked):
          465             if not self.fx: return
          466             self.fx.set_fiat_address_config(checked)
          467             self.window.address_list.refresh_headers()
          468             self.window.address_list.update()
          469 
          470         update_currencies()
          471         update_history_cb()
          472         update_history_capgains_cb()
          473         update_fiat_address_cb()
          474         update_exchanges()
          475         ccy_combo.currentIndexChanged.connect(on_currency)
          476         hist_checkbox.stateChanged.connect(on_history)
          477         hist_capgains_checkbox.stateChanged.connect(on_history_capgains)
          478         fiat_address_checkbox.stateChanged.connect(on_fiat_address)
          479         ex_combo.currentIndexChanged.connect(on_exchange)
          480 
          481         fiat_widgets = []
          482         fiat_widgets.append((QLabel(_('Fiat currency')), ccy_combo))
          483         fiat_widgets.append((QLabel(_('Source')), ex_combo))
          484         fiat_widgets.append((QLabel(_('Show history rates')), hist_checkbox))
          485         fiat_widgets.append((QLabel(_('Show capital gains in history')), hist_capgains_checkbox))
          486         fiat_widgets.append((QLabel(_('Show Fiat balance for addresses')), fiat_address_checkbox))
          487 
          488         tabs_info = [
          489             (gui_widgets, _('General')),
          490             (tx_widgets, _('Transactions')),
          491             (lightning_widgets, _('Lightning')),
          492             (fiat_widgets, _('Fiat')),
          493             (oa_widgets, _('OpenAlias')),
          494         ]
          495         for widgets, name in tabs_info:
          496             tab = QWidget()
          497             tab_vbox = QVBoxLayout(tab)
          498             grid = QGridLayout()
          499             for a,b in widgets:
          500                 i = grid.rowCount()
          501                 if b:
          502                     if a:
          503                         grid.addWidget(a, i, 0)
          504                     grid.addWidget(b, i, 1)
          505                 else:
          506                     grid.addWidget(a, i, 0, 1, 2)
          507             tab_vbox.addLayout(grid)
          508             tab_vbox.addStretch(1)
          509             tabs.addTab(tab, name)
          510 
          511         vbox.addWidget(tabs)
          512         vbox.addStretch(1)
          513         vbox.addLayout(Buttons(CloseButton(self)))
          514         self.setLayout(vbox)
          515         
          516     def set_alias_color(self):
          517         if not self.config.get('alias'):
          518             self.alias_e.setStyleSheet("")
          519             return
          520         if self.window.alias_info:
          521             alias_addr, alias_name, validated = self.window.alias_info
          522             self.alias_e.setStyleSheet((ColorScheme.GREEN if validated else ColorScheme.RED).as_stylesheet(True))
          523         else:
          524             self.alias_e.setStyleSheet(ColorScheme.RED.as_stylesheet(True))
          525 
          526     def on_alias_edit(self):
          527         self.alias_e.setStyleSheet("")
          528         alias = str(self.alias_e.text())
          529         self.config.set_key('alias', alias, True)
          530         if alias:
          531             self.window.fetch_alias()