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()