settings.py (12163B)
1 from kivy.app import App 2 from kivy.factory import Factory 3 from kivy.properties import ObjectProperty 4 from kivy.lang import Builder 5 6 from electrum.util import base_units_list 7 from electrum.i18n import languages 8 from electrum.gui.kivy.i18n import _ 9 from electrum.plugin import run_hook 10 from electrum import coinchooser 11 12 from electrum.gui.kivy import KIVY_GUI_PATH 13 14 from .choice_dialog import ChoiceDialog 15 16 Builder.load_string(''' 17 #:import partial functools.partial 18 #:import _ electrum.gui.kivy.i18n._ 19 20 <SettingsDialog@Popup> 21 id: settings 22 title: _('Electrum Settings') 23 has_pin_code: False 24 use_encryption: False 25 BoxLayout: 26 orientation: 'vertical' 27 ScrollView: 28 GridLayout: 29 id: scrollviewlayout 30 cols:1 31 size_hint: 1, None 32 height: self.minimum_height 33 padding: '10dp' 34 SettingsItem: 35 lang: settings.get_language_name() 36 title: 'Language' + ': ' + str(self.lang) 37 description: _('Language') 38 action: partial(root.language_dialog, self) 39 CardSeparator 40 SettingsItem: 41 status: 'ON' if root.has_pin_code else 'OFF' 42 title: _('PIN code') + ': ' + self.status 43 description: _("Change your PIN code.") if root.has_pin_code else _("Add PIN code") 44 action: partial(root.change_pin_code, self) 45 CardSeparator 46 SettingsItem: 47 bu: app.base_unit 48 title: _('Denomination') + ': ' + self.bu 49 description: _("Base unit for Bitcoin amounts.") 50 action: partial(root.unit_dialog, self) 51 CardSeparator 52 SettingsItem: 53 title: _('Onchain fees') + ': ' + app.fee_status 54 description: _('Choose how transaction fees are estimated') 55 action: lambda dt: app.fee_dialog() 56 CardSeparator 57 SettingsItem: 58 status: root.fx_status() 59 title: _('Fiat Currency') + ': ' + self.status 60 description: _("Display amounts in fiat currency.") 61 action: partial(root.fx_dialog, self) 62 CardSeparator 63 SettingsItem: 64 status: 'ON' if bool(app.plugins.get('labels')) else 'OFF' 65 title: _('Labels Sync') + ': ' + self.status 66 description: _("Save and synchronize your labels.") 67 action: partial(root.plugin_dialog, 'labels', self) 68 CardSeparator 69 SettingsItem: 70 status: 'ON' if app.use_rbf else 'OFF' 71 title: _('Replace-by-fee') + ': ' + self.status 72 description: _("Create replaceable transactions.") 73 message: 74 _('If you check this box, your transactions will be marked as non-final,') \ 75 + ' ' + _('and you will have the possibility, while they are unconfirmed, to replace them with transactions that pays higher fees.') \ 76 + ' ' + _('Note that some merchants do not accept non-final transactions until they are confirmed.') 77 action: partial(root.boolean_dialog, 'use_rbf', _('Replace by fee'), self.message) 78 CardSeparator 79 SettingsItem: 80 status: _('Yes') if app.use_unconfirmed else _('No') 81 title: _('Spend unconfirmed') + ': ' + self.status 82 description: _("Use unconfirmed coins in transactions.") 83 message: _('Spend unconfirmed coins') 84 action: partial(root.boolean_dialog, 'use_unconfirmed', _('Use unconfirmed'), self.message) 85 CardSeparator 86 SettingsItem: 87 status: _('Yes') if app.use_change else _('No') 88 title: _('Use change addresses') + ': ' + self.status 89 description: _("Send your change to separate addresses.") 90 message: _('Send excess coins to change addresses') 91 action: partial(root.boolean_dialog, 'use_change', _('Use change addresses'), self.message) 92 CardSeparator 93 SettingsItem: 94 title: _('Password') 95 description: _('Change your password') if app._use_single_password else _("Change your password for this wallet.") 96 action: root.change_password 97 CardSeparator 98 SettingsItem: 99 status: _('Trampoline') if not app.use_gossip else _('Gossip') 100 title: _('Lightning Routing') + ': ' + self.status 101 description: _("Use trampoline routing or gossip.") 102 action: partial(root.routing_dialog, self) 103 CardSeparator 104 SettingsItem: 105 status: _('Yes') if app.android_backups else _('No') 106 title: _('Backups') + ': ' + self.status 107 description: _("Backup wallet to external storage.") 108 message: _("If this option is checked, a backup of your wallet will be written to external storage everytime you create a new channel. Make sure your wallet is protected with a strong password before you enable this option.") 109 action: partial(root.boolean_dialog, 'android_backups', _('Backups'), self.message) 110 111 # disabled: there is currently only one coin selection policy 112 #CardSeparator 113 #SettingsItem: 114 # status: root.coinselect_status() 115 # title: _('Coin selection') + ': ' + self.status 116 # description: "Coin selection method" 117 # action: partial(root.coinselect_dialog, self) 118 ''') 119 120 121 122 class SettingsDialog(Factory.Popup): 123 124 def __init__(self, app): 125 self.app = app 126 self.plugins = self.app.plugins 127 self.config = self.app.electrum_config 128 Factory.Popup.__init__(self) 129 layout = self.ids.scrollviewlayout 130 layout.bind(minimum_height=layout.setter('height')) 131 # cached dialogs 132 self._fx_dialog = None 133 self._proxy_dialog = None 134 self._language_dialog = None 135 self._unit_dialog = None 136 self._coinselect_dialog = None 137 138 def update(self): 139 self.wallet = self.app.wallet 140 self.use_encryption = self.wallet.has_password() if self.wallet else False 141 self.has_pin_code = self.app.has_pin_code() 142 143 def get_language_name(self): 144 return languages.get(self.config.get('language', 'en_UK'), '') 145 146 def change_password(self, dt): 147 self.app.change_password(self.update) 148 149 def change_pin_code(self, label, dt): 150 self.app.change_pin_code(self.update) 151 152 def language_dialog(self, item, dt): 153 if self._language_dialog is None: 154 l = self.config.get('language', 'en_UK') 155 def cb(key): 156 self.config.set_key("language", key, True) 157 item.lang = self.get_language_name() 158 self.app.language = key 159 self._language_dialog = ChoiceDialog(_('Language'), languages, l, cb) 160 self._language_dialog.open() 161 162 def unit_dialog(self, item, dt): 163 if self._unit_dialog is None: 164 def cb(text): 165 self.app._set_bu(text) 166 item.bu = self.app.base_unit 167 self._unit_dialog = ChoiceDialog(_('Denomination'), base_units_list, 168 self.app.base_unit, cb, keep_choice_order=True) 169 self._unit_dialog.open() 170 171 def routing_dialog(self, item, dt): 172 description = \ 173 _('Lightning payments require finding a path through the Lightning Network.')\ 174 + ' ' + ('You may use trampoline routing, or local routing (gossip).')\ 175 + ' ' + ('Downloading the network gossip uses quite some bandwidth and storage, and is not recommended on mobile devices.')\ 176 + ' ' + ('If you use trampoline, you can only open channels with trampoline nodes.') 177 def cb(text): 178 self.app.use_gossip = (text == 'Gossip') 179 dialog = ChoiceDialog( 180 _('Lightning Routing'), 181 ['Trampoline', 'Gossip'], 182 'Gossip' if self.app.use_gossip else 'Trampoline', 183 cb, description=description, 184 keep_choice_order=True) 185 dialog.open() 186 187 def coinselect_status(self): 188 return coinchooser.get_name(self.app.electrum_config) 189 190 def coinselect_dialog(self, item, dt): 191 if self._coinselect_dialog is None: 192 choosers = sorted(coinchooser.COIN_CHOOSERS.keys()) 193 chooser_name = coinchooser.get_name(self.config) 194 def cb(text): 195 self.config.set_key('coin_chooser', text) 196 item.status = text 197 self._coinselect_dialog = ChoiceDialog(_('Coin selection'), choosers, chooser_name, cb) 198 self._coinselect_dialog.open() 199 200 def proxy_status(self): 201 net_params = self.app.network.get_parameters() 202 proxy = net_params.proxy 203 return proxy.get('host') +':' + proxy.get('port') if proxy else _('None') 204 205 def proxy_dialog(self, item, dt): 206 network = self.app.network 207 if self._proxy_dialog is None: 208 net_params = network.get_parameters() 209 proxy = net_params.proxy 210 def callback(popup): 211 nonlocal net_params 212 if popup.ids.mode.text != 'None': 213 proxy = { 214 'mode':popup.ids.mode.text, 215 'host':popup.ids.host.text, 216 'port':popup.ids.port.text, 217 'user':popup.ids.user.text, 218 'password':popup.ids.password.text 219 } 220 else: 221 proxy = None 222 net_params = net_params._replace(proxy=proxy) 223 network.run_from_another_thread(network.set_parameters(net_params)) 224 item.status = self.proxy_status() 225 popup = Builder.load_file(KIVY_GUI_PATH + '/uix/ui_screens/proxy.kv') 226 popup.ids.mode.text = proxy.get('mode') if proxy else 'None' 227 popup.ids.host.text = proxy.get('host') if proxy else '' 228 popup.ids.port.text = proxy.get('port') if proxy else '' 229 popup.ids.user.text = proxy.get('user') if proxy else '' 230 popup.ids.password.text = proxy.get('password') if proxy else '' 231 popup.on_dismiss = lambda: callback(popup) 232 self._proxy_dialog = popup 233 self._proxy_dialog.open() 234 235 def plugin_dialog(self, name, label, dt): 236 from .checkbox_dialog import CheckBoxDialog 237 def callback(status): 238 self.plugins.enable(name) if status else self.plugins.disable(name) 239 label.status = 'ON' if status else 'OFF' 240 status = bool(self.plugins.get(name)) 241 dd = self.plugins.descriptions.get(name) 242 descr = dd.get('description') 243 fullname = dd.get('fullname') 244 d = CheckBoxDialog(fullname, descr, status, callback) 245 d.open() 246 247 def boolean_dialog(self, name, title, message, dt): 248 from .checkbox_dialog import CheckBoxDialog 249 CheckBoxDialog(title, message, getattr(self.app, name), lambda x: setattr(self.app, name, x)).open() 250 251 def fx_status(self): 252 fx = self.app.fx 253 if fx.is_enabled(): 254 source = fx.exchange.name() 255 ccy = fx.get_currency() 256 return '%s [%s]' %(ccy, source) 257 else: 258 return _('None') 259 260 def fx_dialog(self, label, dt): 261 if self._fx_dialog is None: 262 from .fx_dialog import FxDialog 263 def cb(): 264 label.status = self.fx_status() 265 self._fx_dialog = FxDialog(self.app, self.plugins, self.config, cb) 266 self._fx_dialog.open()