#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2012 thomasv@gitorious
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
" \ + ""+_("WARNING")+": " + _("Never disclose your seed. Never type it on a website.") + "
"
label2 = QLabel(msg2)
label2.setWordWrap(True)
logo = QLabel()
logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
logo.setMaximumWidth(60)
qrw = QRCodeWidget(seed)
ok_button = QPushButton(_("OK"))
ok_button.setDefault(True)
ok_button.clicked.connect(dialog.accept)
grid = QGridLayout()
#main_layout.addWidget(logo, 0, 0)
grid.addWidget(logo, 0, 0)
grid.addWidget(label1, 0, 1)
grid.addWidget(seed_text, 1, 0, 1, 2)
grid.addWidget(qrw, 0, 2, 2, 1)
vbox = QVBoxLayout()
vbox.addLayout(grid)
vbox.addWidget(label2)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(ok_button)
vbox.addLayout(hbox)
dialog.setLayout(vbox)
dialog.exec_()
def show_qrcode(self, data, title = "QR code"):
if not data: return
d = QDialog(self)
d.setModal(1)
d.setWindowTitle(title)
d.setMinimumSize(270, 300)
vbox = QVBoxLayout()
qrw = QRCodeWidget(data)
vbox.addWidget(qrw, 1)
vbox.addWidget(QLabel(data), 0, Qt.AlignHCenter)
hbox = QHBoxLayout()
hbox.addStretch(1)
def print_qr(self):
filename = "qrcode.bmp"
bmp.save_qrcode(qrw.qr, filename)
QMessageBox.information(None, _('Message'), _("QR code saved to file") + " " + filename, _('OK'))
b = QPushButton(_("Save"))
hbox.addWidget(b)
b.clicked.connect(print_qr)
b = QPushButton(_("Close"))
hbox.addWidget(b)
b.clicked.connect(d.accept)
b.setDefault(True)
vbox.addLayout(hbox)
d.setLayout(vbox)
d.exec_()
def do_protect(self, func, args):
if self.wallet.use_encryption:
password = self.password_dialog()
if not password:
return
else:
password = None
if args != (False,):
args = (self,) + args + (password,)
else:
args = (self,password)
apply( func, args)
@protected
def show_private_key(self, address, password):
if not address: return
try:
pk = self.wallet.get_private_key(address, password)
except BaseException, e:
self.show_message(str(e))
return
QMessageBox.information(self, _('Private key'), 'Address'+ ': ' + address + '\n\n' + _('Private key') + ': ' + pk, _('OK'))
@protected
def do_sign(self, address, message, signature, password):
try:
sig = self.wallet.sign_message(str(address.text()), str(message.toPlainText()), password)
signature.setText(sig)
except BaseException, e:
self.show_message(str(e))
def sign_message(self, address):
if not address: return
d = QDialog(self)
d.setModal(1)
d.setWindowTitle(_('Sign Message'))
d.setMinimumSize(410, 290)
tab_widget = QTabWidget()
tab = QWidget()
layout = QGridLayout(tab)
sign_address = QLineEdit()
sign_address.setText(address)
layout.addWidget(QLabel(_('Address')), 1, 0)
layout.addWidget(sign_address, 1, 1)
sign_message = QTextEdit()
layout.addWidget(QLabel(_('Message')), 2, 0)
layout.addWidget(sign_message, 2, 1)
layout.setRowStretch(2,3)
sign_signature = QTextEdit()
layout.addWidget(QLabel(_('Signature')), 3, 0)
layout.addWidget(sign_signature, 3, 1)
layout.setRowStretch(3,1)
hbox = QHBoxLayout()
b = QPushButton(_("Sign"))
hbox.addWidget(b)
b.clicked.connect(lambda: self.do_sign(sign_address, sign_message, sign_signature))
b = QPushButton(_("Close"))
b.clicked.connect(d.accept)
hbox.addWidget(b)
layout.addLayout(hbox, 4, 1)
tab_widget.addTab(tab, _("Sign"))
tab = QWidget()
layout = QGridLayout(tab)
verify_address = QLineEdit()
layout.addWidget(QLabel(_('Address')), 1, 0)
layout.addWidget(verify_address, 1, 1)
verify_message = QTextEdit()
layout.addWidget(QLabel(_('Message')), 2, 0)
layout.addWidget(verify_message, 2, 1)
layout.setRowStretch(2,3)
verify_signature = QTextEdit()
layout.addWidget(QLabel(_('Signature')), 3, 0)
layout.addWidget(verify_signature, 3, 1)
layout.setRowStretch(3,1)
def do_verify():
try:
self.wallet.verify_message(verify_address.text(), str(verify_signature.toPlainText()), str(verify_message.toPlainText()))
self.show_message(_("Signature verified"))
except BaseException, e:
self.show_message(str(e))
return
hbox = QHBoxLayout()
b = QPushButton(_("Verify"))
b.clicked.connect(do_verify)
hbox.addWidget(b)
b = QPushButton(_("Close"))
b.clicked.connect(d.accept)
hbox.addWidget(b)
layout.addLayout(hbox, 4, 1)
tab_widget.addTab(tab, _("Verify"))
vbox = QVBoxLayout()
vbox.addWidget(tab_widget)
d.setLayout(vbox)
d.exec_()
def question(self, msg):
return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
def show_message(self, msg):
QMessageBox.information(self, _('Message'), msg, _('OK'))
def password_dialog(self ):
d = QDialog(self)
d.setModal(1)
pw = QLineEdit()
pw.setEchoMode(2)
vbox = QVBoxLayout()
msg = _('Please enter your password')
vbox.addWidget(QLabel(msg))
grid = QGridLayout()
grid.setSpacing(8)
grid.addWidget(QLabel(_('Password')), 1, 0)
grid.addWidget(pw, 1, 1)
vbox.addLayout(grid)
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
self.run_hook('password_dialog', pw, grid, 1)
if not d.exec_(): return
return unicode(pw.text())
@staticmethod
def change_password_dialog( wallet, parent=None ):
if not wallet.seed:
QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
return
d = QDialog(parent)
d.setModal(1)
pw = QLineEdit()
pw.setEchoMode(2)
new_pw = QLineEdit()
new_pw.setEchoMode(2)
conf_pw = QLineEdit()
conf_pw.setEchoMode(2)
vbox = QVBoxLayout()
if parent:
msg = (_('Your wallet is encrypted. Use this dialog to change your password.')+'\n'\
+_('To disable wallet encryption, enter an empty new password.')) \
if wallet.use_encryption else _('Your wallet keys are not encrypted')
else:
msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
+_("Leave these fields empty if you want to disable encryption.")
vbox.addWidget(QLabel(msg))
grid = QGridLayout()
grid.setSpacing(8)
if wallet.use_encryption:
grid.addWidget(QLabel(_('Password')), 1, 0)
grid.addWidget(pw, 1, 1)
grid.addWidget(QLabel(_('New Password')), 2, 0)
grid.addWidget(new_pw, 2, 1)
grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
grid.addWidget(conf_pw, 3, 1)
vbox.addLayout(grid)
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
if not d.exec_(): return
password = unicode(pw.text()) if wallet.use_encryption else None
new_password = unicode(new_pw.text())
new_password2 = unicode(conf_pw.text())
try:
seed = wallet.decode_seed(password)
except:
QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
return
if new_password != new_password2:
QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
return ElectrumWindow.change_password_dialog(wallet, parent) # Retry
wallet.update_password(seed, password, new_password)
if parent:
icon = QIcon(":icons/lock.png") if wallet.use_encryption else QIcon(":icons/unlock.png")
parent.password_button.setIcon( icon )
@staticmethod
def seed_dialog(wallet, parent=None):
d = QDialog(parent)
d.setModal(1)
vbox = QVBoxLayout()
msg = _("Please enter your wallet seed (or your master public key if you want to create a watching-only wallet)." + '\n')
vbox.addWidget(QLabel(msg))
grid = QGridLayout()
grid.setSpacing(8)
seed_e = QLineEdit()
grid.addWidget(QLabel(_('Seed or master public key')), 1, 0)
grid.addWidget(seed_e, 1, 1)
grid.addWidget(HelpButton(_("Your seed can be entered as a mnemonic (sequence of words), or as a hexadecimal string.")), 1, 3)
gap_e = QLineEdit()
gap_e.setText("5")
grid.addWidget(QLabel(_('Gap limit')), 2, 0)
grid.addWidget(gap_e, 2, 1)
grid.addWidget(HelpButton(_('Keep the default value unless you modified this parameter in your wallet.')), 2, 3)
gap_e.textChanged.connect(lambda: numbify(gap_e,True))
vbox.addLayout(grid)
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
if not d.exec_(): return
try:
gap = int(unicode(gap_e.text()))
except:
QMessageBox.warning(None, _('Error'), 'error', 'OK')
return
try:
seed = str(seed_e.text())
seed.decode('hex')
except:
print_error("Warning: Not hex, trying decode")
try:
seed = mnemonic.mn_decode( seed.split(' ') )
except:
QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
return
if not seed:
QMessageBox.warning(None, _('Error'), _('No seed'), _('OK'))
return
return seed, gap
def generate_transaction_information_widget(self, tx):
tabs = QTabWidget(self)
tab1 = QWidget()
grid_ui = QGridLayout(tab1)
grid_ui.setColumnStretch(0,1)
tabs.addTab(tab1, _('Outputs') )
tree_widget = MyTreeWidget(self)
tree_widget.setColumnCount(2)
tree_widget.setHeaderLabels( [_('Address'), _('Amount')] )
tree_widget.setColumnWidth(0, 300)
tree_widget.setColumnWidth(1, 50)
for address, value in tx.outputs:
item = QTreeWidgetItem( [address, "%s" % ( format_satoshis(value))] )
tree_widget.addTopLevelItem(item)
tree_widget.setMaximumHeight(100)
grid_ui.addWidget(tree_widget)
tab2 = QWidget()
grid_ui = QGridLayout(tab2)
grid_ui.setColumnStretch(0,1)
tabs.addTab(tab2, _('Inputs') )
tree_widget = MyTreeWidget(self)
tree_widget.setColumnCount(2)
tree_widget.setHeaderLabels( [ _('Address'), _('Previous output')] )
for input_line in tx.inputs:
item = QTreeWidgetItem( [ str(input_line["address"]), str(input_line["prevout_hash"])] )
tree_widget.addTopLevelItem(item)
tree_widget.setMaximumHeight(100)
grid_ui.addWidget(tree_widget)
return tabs
def tx_dict_from_text(self, txt):
try:
tx_dict = json.loads(str(txt))
assert "hex" in tx_dict.keys()
assert "complete" in tx_dict.keys()
if not tx_dict["complete"]:
assert "input_info" in tx_dict.keys()
except:
QMessageBox.critical(None, "Unable to parse transaction", _("Electrum was unable to parse your transaction"))
return None
return tx_dict
def read_tx_from_file(self):
fileName = self.getOpenFileName(_("Select your transaction file"), "*.txn")
if not fileName:
return
try:
with open(fileName, "r") as f:
file_content = f.read()
except (ValueError, IOError, os.error), reason:
QMessageBox.critical(None,"Unable to read file or no transaction found", _("Electrum was unable to open your transaction file") + "\n" + str(reason))
return self.tx_dict_from_text(file_content)
@protected
def sign_raw_transaction(self, tx, input_info, dialog ="", password = ""):
try:
self.wallet.signrawtransaction(tx, input_info, [], password)
fileName = self.getSaveFileName(_("Select where to save your signed transaction"), 'signed_%s.txn' % (tx.hash()[0:8]), "*.txn")
if fileName:
with open(fileName, "w+") as f:
f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
self.show_message(_("Transaction saved successfully"))
if dialog:
dialog.done(0)
except BaseException, e:
self.show_message(str(e))
def send_raw_transaction(self, raw_tx, dialog = ""):
result, result_message = self.wallet.sendtx( raw_tx )
if result:
self.show_message("Transaction successfully sent: %s" % (result_message))
if dialog:
dialog.done(0)
else:
self.show_message("There was a problem sending your transaction:\n %s" % (result_message))
def do_process_from_text(self):
text = text_dialog(self, _('Input raw transaction'), _("Transaction:"), _("Load transaction"))
if not text:
return
tx_dict = self.tx_dict_from_text(text)
if tx_dict:
self.create_process_transaction_window(tx_dict)
def do_process_from_file(self):
tx_dict = self.read_tx_from_file()
if tx_dict:
self.create_process_transaction_window(tx_dict)
def create_process_transaction_window(self, tx_dict):
tx = Transaction(tx_dict["hex"])
dialog = QDialog(self)
dialog.setMinimumWidth(500)
dialog.setWindowTitle(_('Process raw transaction'))
dialog.setModal(1)
l = QGridLayout()
dialog.setLayout(l)
l.addWidget(QLabel(_("Transaction status:")), 3,0)
l.addWidget(QLabel(_("Actions")), 4,0)
if tx_dict["complete"] == False:
l.addWidget(QLabel(_("Unsigned")), 3,1)
if self.wallet.seed :
b = QPushButton("Sign transaction")
input_info = json.loads(tx_dict["input_info"])
b.clicked.connect(lambda: self.sign_raw_transaction(tx, input_info, dialog))
l.addWidget(b, 4, 1)
else:
l.addWidget(QLabel(_("Wallet is de-seeded, can't sign.")), 4,1)
else:
l.addWidget(QLabel(_("Signed")), 3,1)
b = QPushButton("Broadcast transaction")
b.clicked.connect(lambda: self.send_raw_transaction(tx, dialog))
l.addWidget(b,4,1)
l.addWidget( self.generate_transaction_information_widget(tx), 0,0,2,3)
cancelButton = QPushButton(_("Cancel"))
cancelButton.clicked.connect(lambda: dialog.done(0))
l.addWidget(cancelButton, 4,2)
dialog.exec_()
@protected
def do_export_privkeys(self, password):
self.show_message("%s\n%s\n%s" % (_("WARNING: ALL your private keys are secret."), _("Exposing a single private key can compromise your entire wallet!"), _("In particular, DO NOT use 'redeem private key' services proposed by third parties.")))
try:
select_export = _('Select file to export your private keys to')
fileName = self.getSaveFileName(select_export, 'electrum-private-keys.csv', "*.csv")
if fileName:
with open(fileName, "w+") as csvfile:
transaction = csv.writer(csvfile)
transaction.writerow(["address", "private_key"])
for addr, pk in self.wallet.get_private_keys(self.wallet.addresses(True), password).items():
transaction.writerow(["%34s"%addr,pk])
self.show_message(_("Private keys exported."))
except (IOError, os.error), reason:
export_error_label = _("Electrum was unable to produce a private key-export.")
QMessageBox.critical(None,"Unable to create csv", export_error_label + "\n" + str(reason))
except BaseException, e:
self.show_message(str(e))
return
def do_import_labels(self):
labelsFile = self.getOpenFileName(_("Open labels file"), "*.dat")
if not labelsFile: return
try:
f = open(labelsFile, 'r')
data = f.read()
f.close()
for key, value in json.loads(data).items():
self.wallet.labels[key] = value
self.wallet.save()
QMessageBox.information(None, _("Labels imported"), _("Your labels were imported from")+" '%s'" % str(labelsFile))
except (IOError, os.error), reason:
QMessageBox.critical(None, _("Unable to import labels"), _("Electrum was unable to import your labels.")+"\n" + str(reason))
def do_export_labels(self):
labels = self.wallet.labels
try:
fileName = self.getSaveFileName(_("Select file to save your labels"), 'electrum_labels.dat', "*.dat")
if fileName:
with open(fileName, 'w+') as f:
json.dump(labels, f)
QMessageBox.information(None, "Labels exported", _("Your labels where exported to")+" '%s'" % str(fileName))
except (IOError, os.error), reason:
QMessageBox.critical(None, "Unable to export labels", _("Electrum was unable to export your labels.")+"\n" + str(reason))
def do_export_history(self):
from gui_lite import csv_transaction
csv_transaction(self.wallet)
@protected
def do_import_privkey(self, password):
if not self.wallet.imported_keys:
r = QMessageBox.question(None, _('Warning'), ''+_('Warning') +':\n
'+ _('Imported keys are not recoverable from seed.') + ' ' \
+ _('If you ever need to restore your wallet from its seed, these keys will be lost.') + '
' \ + _('In addition, when you send bitcoins from one of your imported addresses, the "change" will be sent to an address derived from your seed, unless you disabled this option.') + '
' \ + _('Are you sure you understand what you are doing?'), 3, 4) if r == 4: return text = text_dialog(self, _('Import private keys'), _("Enter private keys")+':', _("Import")) if not text: return text = str(text).split() badkeys = [] addrlist = [] for key in text: try: addr = self.wallet.import_key(key, password) except BaseException as e: badkeys.append(key) continue if not addr: badkeys.append(key) else: addrlist.append(addr) if addrlist: QMessageBox.information(self, _('Information'), _("The following addresses were added") + ':\n' + '\n'.join(addrlist)) if badkeys: QMessageBox.critical(self, _('Error'), _("The following inputs could not be imported") + ':\n'+ '\n'.join(badkeys)) self.update_receive_tab() self.update_history_tab() def settings_dialog(self): d = QDialog(self) d.setWindowTitle(_('Electrum Settings')) d.setModal(1) vbox = QVBoxLayout() tabs = QTabWidget(self) self.settings_tab = tabs vbox.addWidget(tabs) tab1 = QWidget() grid_ui = QGridLayout(tab1) grid_ui.setColumnStretch(0,1) tabs.addTab(tab1, _('Display') ) nz_label = QLabel(_('Display zeros')) grid_ui.addWidget(nz_label, 0, 0) nz_e = QLineEdit() nz_e.setText("%d"% self.wallet.num_zeros) grid_ui.addWidget(nz_e, 0, 1) msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"') grid_ui.addWidget(HelpButton(msg), 0, 2) nz_e.textChanged.connect(lambda: numbify(nz_e,True)) if not self.config.is_modifiable('num_zeros'): for w in [nz_e, nz_label]: w.setEnabled(False) lang_label=QLabel(_('Language') + ':') grid_ui.addWidget(lang_label, 1, 0) lang_combo = QComboBox() from i18n import languages lang_combo.addItems(languages.values()) try: index = languages.keys().index(self.config.get("language",'')) except: index = 0 lang_combo.setCurrentIndex(index) grid_ui.addWidget(lang_combo, 1, 1) grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart).')+' '), 1, 2) if not self.config.is_modifiable('language'): for w in [lang_combo, lang_label]: w.setEnabled(False) currencies = self.exchanger.get_currencies() currencies.insert(0, "None") cur_label=QLabel(_('Currency') + ':') grid_ui.addWidget(cur_label , 2, 0) cur_combo = QComboBox() cur_combo.addItems(currencies) try: index = currencies.index(self.config.get('currency', "None")) except: index = 0 cur_combo.setCurrentIndex(index) grid_ui.addWidget(cur_combo, 2, 1) grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes.')+' '), 2, 2) view_label=QLabel(_('Receive Tab') + ':') grid_ui.addWidget(view_label , 3, 0) view_combo = QComboBox() view_combo.addItems([_('Simple'), _('Advanced')]) view_combo.setCurrentIndex(self.expert_mode) grid_ui.addWidget(view_combo, 3, 1) hh = _('This selects the interaction mode of the "Receive" tab.')+' ' + '\n\n' \ + _('Simple') + ': ' + _('Show only addresses and labels.') + '\n\n' \ + _('Advanced') + ': ' + _('Show address balances and add extra menu items to freeze/prioritize addresses.') + '\n\n' grid_ui.addWidget(HelpButton(hh), 3, 2) grid_ui.setRowStretch(4,1) # wallet tab tab2 = QWidget() grid_wallet = QGridLayout(tab2) grid_wallet.setColumnStretch(0,1) tabs.addTab(tab2, _('Wallet') ) fee_label = QLabel(_('Transaction fee')) grid_wallet.addWidget(fee_label, 0, 0) fee_e = QLineEdit() fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) ) grid_wallet.addWidget(fee_e, 0, 2) msg = _('Fee per kilobyte of transaction.') + ' ' \ + _('Recommended value') + ': 0.0001' grid_wallet.addWidget(HelpButton(msg), 0, 3) fee_e.textChanged.connect(lambda: numbify(fee_e,False)) if not self.config.is_modifiable('fee_per_kb'): for w in [fee_e, fee_label]: w.setEnabled(False) usechange_label = QLabel(_('Use change addresses')) grid_wallet.addWidget(usechange_label, 1, 0) usechange_combo = QComboBox() usechange_combo.addItems([_('Yes'), _('No')]) usechange_combo.setCurrentIndex(not self.wallet.use_change) grid_wallet.addWidget(usechange_combo, 1, 2) grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions.')+' '), 1, 3) if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False) gap_label = QLabel(_('Gap limit')) grid_wallet.addWidget(gap_label, 2, 0) gap_e = QLineEdit() gap_e.setText("%d"% self.wallet.gap_limit) grid_wallet.addWidget(gap_e, 2, 2) msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \ + _('You may increase it if you need more receiving addresses.') + '\n\n' \ + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \ + _('Given the current status of your address sequence, the minimum gap limit you can use is:')+' ' + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \ + _('Warning') + ': ' \ + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \ + _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n' grid_wallet.addWidget(HelpButton(msg), 2, 3) gap_e.textChanged.connect(lambda: numbify(nz_e,True)) if not self.config.is_modifiable('gap_limit'): for w in [gap_e, gap_label]: w.setEnabled(False) grid_wallet.setRowStretch(3,1) # import/export tab tab3 = QWidget() grid_io = QGridLayout(tab3) grid_io.setColumnStretch(0,1) tabs.addTab(tab3, _('Import/Export') ) grid_io.addWidget(QLabel(_('Labels')), 1, 0) grid_io.addWidget(EnterButton(_("Export"), self.do_export_labels), 1, 1) grid_io.addWidget(EnterButton(_("Import"), self.do_import_labels), 1, 2) grid_io.addWidget(HelpButton(_('Export your labels as json')), 1, 3) grid_io.addWidget(QLabel(_('History')), 2, 0) grid_io.addWidget(EnterButton(_("Export"), self.do_export_history), 2, 1) grid_io.addWidget(HelpButton(_('Export your transaction history as csv')), 2, 3) grid_io.addWidget(QLabel(_('Private keys')), 3, 0) grid_io.addWidget(EnterButton(_("Export"), self.do_export_privkeys), 3, 1) grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2) grid_io.addWidget(HelpButton(_('Import private key')), 3, 3) grid_io.addWidget(QLabel(_('Master Public Key')), 4, 0) grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1) grid_io.addWidget(HelpButton(_('Your Master Public Key can be used to create receiving addresses, but not to sign transactions.') + ' ' \ + _('If you give it to someone, they will be able to see your transactions, but not to spend your money.') + ' ' \ + _('If you restore your wallet from it, a watching-only (deseeded) wallet will be created.')), 4, 3) grid_io.addWidget(QLabel(_("Load transaction")), 5, 0) grid_io.addWidget(EnterButton(_("From file"), self.do_process_from_file), 5, 1) grid_io.addWidget(EnterButton(_("From text"), self.do_process_from_text), 5, 2) grid_io.addWidget(HelpButton(_("This will give you the option to sign or broadcast a transaction based on it's status.")), 5, 3) grid_io.setRowStretch(5,1) # plugins if self.plugins: tab5 = QScrollArea() grid_plugins = QGridLayout(tab5) grid_plugins.setColumnStretch(0,1) tabs.addTab(tab5, _('Plugins') ) def mk_toggle(cb, p): return lambda: cb.setChecked(p.toggle()) for i, p in enumerate(self.plugins): try: name, description = p.get_info() cb = QCheckBox(name) cb.setDisabled(not p.is_available()) cb.setChecked(p.is_enabled()) cb.clicked.connect(mk_toggle(cb,p)) grid_plugins.addWidget(cb, i, 0) if p.requires_settings(): grid_plugins.addWidget(EnterButton(_('Settings'), p.settings_dialog), i, 1) grid_plugins.addWidget(HelpButton(description), i, 2) except: print_msg("Error: cannot display plugin", p) traceback.print_exc(file=sys.stdout) grid_plugins.setRowStretch(i+1,1) self.run_hook('create_settings_tab', tabs) vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) # run the dialog if not d.exec_(): return fee = unicode(fee_e.text()) try: fee = int( 100000000 * Decimal(fee) ) except: QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK')) return if self.wallet.fee != fee: self.wallet.fee = fee self.wallet.save() nz = unicode(nz_e.text()) try: nz = int( nz ) if nz>8: nz=8 except: QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK')) return if self.wallet.num_zeros != nz: self.wallet.num_zeros = nz self.config.set_key('num_zeros', nz, True) self.update_history_tab() self.update_receive_tab() usechange_result = usechange_combo.currentIndex() == 0 if self.wallet.use_change != usechange_result: self.wallet.use_change = usechange_result self.config.set_key('use_change', self.wallet.use_change, True) try: n = int(gap_e.text()) except: QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK')) return if self.wallet.gap_limit != n: r = self.wallet.change_gap_limit(n) if r: self.update_receive_tab() self.config.set_key('gap_limit', self.wallet.gap_limit, True) else: QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK')) need_restart = False lang_request = languages.keys()[lang_combo.currentIndex()] if lang_request != self.config.get('language'): self.config.set_key("language", lang_request, True) need_restart = True cur_request = str(currencies[cur_combo.currentIndex()]) if cur_request != self.config.get('currency', "None"): self.config.set_key('currency', cur_request, True) self.update_wallet() self.run_hook('close_settings_dialog') if need_restart: QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK')) self.receive_tab_set_mode(view_combo.currentIndex()) @staticmethod def network_dialog(wallet, parent=None): interface = wallet.interface if parent: if interface.is_connected: status = _("Connected to")+" %s\n%d "%(interface.host, wallet.verifier.height)+_("blocks") else: status = _("Not connected") server = interface.server else: import random status = _("Please choose a server.") + "\n" + _("Select 'Cancel' if you are offline.") server = interface.server plist, servers_list = interface.get_servers_list() d = QDialog(parent) d.setModal(1) d.setWindowTitle(_('Server')) d.setMinimumSize(375, 20) vbox = QVBoxLayout() vbox.setSpacing(30) hbox = QHBoxLayout() l = QLabel() l.setPixmap(QPixmap(":icons/network.png")) hbox.addStretch(10) hbox.addWidget(l) hbox.addWidget(QLabel(status)) hbox.addStretch(50) vbox.addLayout(hbox) # grid layout grid = QGridLayout() grid.setSpacing(8) vbox.addLayout(grid) # server server_protocol = QComboBox() server_host = QLineEdit() server_host.setFixedWidth(200) server_port = QLineEdit() server_port.setFixedWidth(60) protocol_names = ['TCP', 'HTTP', 'TCP/SSL', 'HTTPS'] protocol_letters = 'thsg' DEFAULT_PORTS = {'t':'50001', 's':'50002', 'h':'8081', 'g':'8082'} server_protocol.addItems(protocol_names) grid.addWidget(QLabel(_('Server') + ':'), 0, 0) grid.addWidget(server_protocol, 0, 1) grid.addWidget(server_host, 0, 2) grid.addWidget(server_port, 0, 3) def change_protocol(p): protocol = protocol_letters[p] host = unicode(server_host.text()) pp = plist.get(host,DEFAULT_PORTS) if protocol not in pp.keys(): protocol = pp.keys()[0] port = pp[protocol] server_host.setText( host ) server_port.setText( port ) server_protocol.connect(server_protocol, SIGNAL('currentIndexChanged(int)'), change_protocol) label = _('Active Servers') if wallet.interface.servers else _('Default Servers') servers_list_widget = QTreeWidget(parent) servers_list_widget.setHeaderLabels( [ label, _('Type') ] ) servers_list_widget.setMaximumHeight(150) servers_list_widget.setColumnWidth(0, 240) for _host in servers_list.keys(): _type = 'P' if servers_list[_host].get('pruning') else 'F' servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ _host, _type ] )) def change_server(host, protocol=None): pp = plist.get(host,DEFAULT_PORTS) if protocol: port = pp.get(protocol) if not port: protocol = None if not protocol: if 't' in pp.keys(): protocol = 't' port = pp.get(protocol) else: protocol = pp.keys()[0] port = pp.get(protocol) server_host.setText( host ) server_port.setText( port ) server_protocol.setCurrentIndex(protocol_letters.index(protocol)) if not plist: return for p in protocol_letters: i = protocol_letters.index(p) j = server_protocol.model().index(i,0) if p not in pp.keys() and interface.is_connected: server_protocol.model().setData(j, QtCore.QVariant(0), QtCore.Qt.UserRole-1) else: server_protocol.model().setData(j, QtCore.QVariant(33), QtCore.Qt.UserRole-1) if server: host, port, protocol = server.split(':') change_server(host,protocol) servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), lambda x: change_server(unicode(x.text(0)))) grid.addWidget(servers_list_widget, 1, 1, 1, 3) if not wallet.config.is_modifiable('server'): for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False) # auto cycle autocycle_cb = QCheckBox(_('Try random servers if disconnected')) autocycle_cb.setChecked(wallet.config.get('auto_cycle', False)) grid.addWidget(autocycle_cb, 3, 1, 3, 2) if not wallet.config.is_modifiable('auto_cycle'): autocycle_cb.setEnabled(False) # proxy setting proxy_mode = QComboBox() proxy_host = QLineEdit() proxy_host.setFixedWidth(200) proxy_port = QLineEdit() proxy_port.setFixedWidth(60) proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP']) def check_for_disable(index = False): if proxy_mode.currentText() != 'NONE': proxy_host.setEnabled(True) proxy_port.setEnabled(True) else: proxy_host.setEnabled(False) proxy_port.setEnabled(False) check_for_disable() proxy_mode.connect(proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable) if not wallet.config.is_modifiable('proxy'): for w in [proxy_host, proxy_port, proxy_mode]: w.setEnabled(False) proxy_config = interface.proxy if interface.proxy else { "mode":"none", "host":"localhost", "port":"8080"} proxy_mode.setCurrentIndex(proxy_mode.findText(str(proxy_config.get("mode").upper()))) proxy_host.setText(proxy_config.get("host")) proxy_port.setText(proxy_config.get("port")) grid.addWidget(QLabel(_('Proxy') + ':'), 2, 0) grid.addWidget(proxy_mode, 2, 1) grid.addWidget(proxy_host, 2, 2) grid.addWidget(proxy_port, 2, 3) # buttons vbox.addLayout(ok_cancel_buttons(d)) d.setLayout(vbox) if not d.exec_(): return server = unicode( server_host.text() ) + ':' + unicode( server_port.text() ) + ':' + (protocol_letters[server_protocol.currentIndex()]) if proxy_mode.currentText() != 'NONE': proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) } else: proxy = None wallet.config.set_key("proxy", proxy, True) wallet.config.set_key("server", server, True) interface.set_server(server, proxy) wallet.config.set_key('auto_cycle', autocycle_cb.isChecked(), True) return True def closeEvent(self, event): g = self.geometry() self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True) self.save_column_widths() self.config.set_key("column_widths", self.column_widths, True) self.config.set_key("console-history",self.console.history[-50:]) event.accept() class ElectrumGui: def __init__(self, wallet, config, app=None): self.wallet = wallet self.config = config if app is None: self.app = QApplication(sys.argv) def restore_or_create(self): msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?") r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2) if r==2: return None return 'restore' if r==1 else 'create' def seed_dialog(self): return ElectrumWindow.seed_dialog( self.wallet ) def network_dialog(self): return ElectrumWindow.network_dialog( self.wallet, parent=None ) def show_seed(self): ElectrumWindow.show_seed(self.wallet.seed) def password_dialog(self): if self.wallet.seed: ElectrumWindow.change_password_dialog(self.wallet) def restore_wallet(self): wallet = self.wallet # wait until we are connected, because the user might have selected another server if not wallet.interface.is_connected: waiting = lambda: False if wallet.interface.is_connected else "%s \n" % (_("Connecting...")) waiting_dialog(waiting) waiting = lambda: False if wallet.is_up_to_date() else "%s\n%s %d\n%s %.1f"\ %(_("Please wait..."),_("Addresses generated:"),len(wallet.addresses(True)),_("Kilobytes received:"), wallet.interface.bytes_received/1024.) wallet.set_up_to_date(False) wallet.interface.poke('synchronizer') waiting_dialog(waiting) if wallet.is_found(): print_error( "Recovery successful" ) else: QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK')) return True def main(self,url): s = Timer() s.start() w = ElectrumWindow(self.wallet, self.config) if url: w.set_url(url) w.app = self.app w.connect_slots(s) w.update_wallet() w.show() self.app.exec_()