import sys, time, datetime, re
from i18n import _
from util import print_error
+import os.path, json, util
try:
import PyQt4
from wallet import format_satoshis
import bmp, mnemonic, pyqrnative, qrscanner
+import exchange_rate
from decimal import Decimal
def __init__(self, wallet, config):
QMainWindow.__init__(self)
+ self.lite = None
self.wallet = wallet
self.config = config
self.wallet.interface.register_callback('updated', self.update_callback)
self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
#self.connect(self, SIGNAL('editamount'), self.edit_amount)
self.history_list.setFocus(True)
+
+ self.exchanger = exchange_rate.Exchanger(self)
+ self.connect(self, SIGNAL("refresh_balance()"), self.update_wallet)
# dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
if platform.system() == 'Windows':
c, u = self.wallet.get_balance()
text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
+ text += self.create_quote_text(Decimal(c+u)/100000000)
icon = QIcon(":icons/status_connected.png")
else:
text = _( "Not connected" )
icon = QIcon(":icons/status_disconnected.png")
- if self.funds_error:
- text = _( "Not enough funds" )
-
+ self.status_text = text
self.statusBar().showMessage(text)
self.status_button.setIcon( icon )
self.update_contacts_tab()
self.update_completions()
-
+ def create_quote_text(self, btc_balance):
+ quote_currency = self.config.get("currency", "None")
+ quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
+ if quote_balance is None:
+ quote_text = ""
+ else:
+ quote_text = " (%.2f %s)" % (quote_balance, quote_currency)
+ return quote_text
+
def create_history_tab(self):
self.history_list = l = MyTreeWidget(self)
l.setColumnCount(5)
self.history_list.selectedIndexes()
item = self.history_list.currentItem()
if not item: return
- tx_hash = str(item.toolTip(0))
+ tx_hash = str(item.data(0, Qt.UserRole).toString())
if not tx_hash: return
menu = QMenu()
menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
if value < 0:
item.setForeground(3, QBrush(QColor("#BC1E1E")))
if tx_hash:
- item.setToolTip(0, tx_hash)
+ item.setData(0, Qt.UserRole, tx_hash)
+ item.setToolTip(0, "%d %s\nTxId:%s" % (conf, _('Confirmations'), tx_hash) )
if is_default_label:
item.setForeground(2, QBrush(QColor('grey')))
if inputs:
palette = QPalette()
palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
+ text = self.status_text
else:
palette = QPalette()
palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
self.funds_error = True
+ text = _( "Not enough funds" )
+
+ self.statusBar().showMessage(text)
self.amount_e.setPalette(palette)
self.fee_e.setPalette(palette)
- self.update_wallet()
self.amount_e.textChanged.connect(lambda: entry_changed(False) )
self.fee_e.textChanged.connect(lambda: entry_changed(True) )
return w
+ def delete_imported_key(self, addr):
+ if self.question("Do you want to remove %s from your wallet?"%addr):
+ self.wallet.imported_keys.pop(addr)
+ self.update_receive_tab()
+ self.update_history_tab()
+ self.wallet.save()
+
+
def create_receive_menu(self, position):
# fixme: this function apparently has a side effect.
# if it is not called the menu pops up several times
menu.addAction(_("View QR"), lambda: ElectrumWindow.show_qrcode("Address","bitcoin:"+addr) )
menu.addAction(_("Edit label"), lambda: self.edit_label(True))
menu.addAction(_("Sign message"), lambda: self.sign_message(addr))
+ if addr in self.wallet.imported_keys:
+ menu.addAction(_("Remove from wallet"), lambda: self.delete_imported_key(addr))
if self.receive_tab_mode == 1:
t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
return textbox
def create_status_bar(self):
+ self.status_text = ""
sb = QStatusBar()
sb.setFixedHeight(35)
+ qtVersion = qVersion()
+ if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
+ sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), "Switch to Lite Mode", self.go_lite ) )
if self.wallet.seed:
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
sb.addPermanentWidget( self.status_button )
self.setStatusBar(sb)
+
+ def go_lite(self):
+ import gui_lite
+ self.hide()
+ if self.lite:
+ self.lite.mini.show()
+ else:
+ self.lite = gui_lite.ElectrumGui(self.wallet, self.config, self)
+ self.lite.main(None)
def new_contact_dialog(self):
text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
password = None
try:
- seed = wallet.pw_decode(wallet.seed, password)
+ seed = wallet.decode_seed(password)
except:
- QMessageBox.warning(parent, _('Error'),
- _('Incorrect Password'), _('OK'))
+ QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
return
dialog = QDialog(None)
d = QDialog(self)
d.setModal(1)
d.setWindowTitle('Sign Message')
- d.setMinimumSize(270, 350)
+ 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, 2, 1)
+ layout.addWidget(sign_message, 2, 1)
+ layout.setRowStretch(2,3)
- sign_signature = QLineEdit()
+ sign_signature = QTextEdit()
layout.addWidget(QLabel(_('Signature')), 3, 0)
layout.addWidget(sign_signature, 3, 1)
+ layout.setRowStretch(3,1)
def do_sign():
if self.wallet.use_encryption:
password = None
try:
- signature = self.wallet.sign_message(sign_address.text(), str(sign_message.toPlainText()), password)
+ signature = self.wallet.sign_message(str(sign_address.text()), str(sign_message.toPlainText()), password)
sign_signature.setText(signature)
except BaseException, e:
self.show_message(str(e))
verify_message = QTextEdit()
layout.addWidget(QLabel(_('Message')), 2, 0)
- layout.addWidget(verify_message, 2, 1, 2, 1)
+ layout.addWidget(verify_message, 2, 1)
+ layout.setRowStretch(2,3)
- verify_signature = QLineEdit()
+ 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(), verify_signature.text(), str(verify_message.toPlainText()))
+ 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))
new_password2 = unicode(conf_pw.text())
try:
- seed = wallet.pw_decode( wallet.seed, password)
+ seed = wallet.decode_seed(password)
except:
QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
return
return True
+ def do_import_labels(self):
+ labelsFile = QFileDialog.getOpenFileName(QWidget(), "Open text file", util.user_dir(), self.tr("Text Files (labels.dat)"))
+ if not labelsFile: return
+ try:
+ f = open(labelsFile, 'r')
+ data = f.read()
+ f.close()
+ self.wallet.labels = json.loads(data)
+ self.wallet.save()
+ QMessageBox.information(None, "Labels imported", "Your labels where imported from '%s'" % str(labelsFile))
+ 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_labels(self):
+ labels = self.wallet.labels
+ try:
+ labelsFile = util.user_dir() + '/labels.dat'
+ f = open(labelsFile, 'w+')
+ json.dump(labels, f)
+ f.close()
+ QMessageBox.information(None, "Labels exported", "Your labels where exported to '%s'" % str(labelsFile))
+ 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)
+
+ def do_import_privkey(self):
+ if not self.wallet.imported_keys:
+ r = QMessageBox.question(None, _('Warning'), _('Warning: Imported keys are not recoverable from seed.') + ' ' \
+ + _('If you ever need to restore your wallet from its seed, these keys will be lost.') + '\n\n' \
+ + _('Are you sure you understand what you are doing?'), 3, 4)
+ if r == 4: return
+
+ text, ok = QInputDialog.getText(self, _('Import private key'), _('Private Key') + ':')
+ if not ok: return
+ sec = str(text)
+ if self.wallet.use_encryption:
+ password = self.password_dialog()
+ if not password:
+ return
+ else:
+ password = None
+ try:
+ addr = self.wallet.import_key(sec, password)
+ if not addr:
+ QMessageBox.critical(None, "Unable to import key", "error")
+ else:
+ QMessageBox.information(None, "Key imported", addr)
+ self.update_receive_tab()
+ self.update_history_tab()
+ except BaseException as e:
+ QMessageBox.critical(None, "Unable to import key", str(e))
def settings_dialog(self):
d = QDialog(self)
tabs = QTabWidget(self)
vbox.addWidget(tabs)
- tab2 = QWidget()
- grid_ui = QGridLayout(tab2)
+ tab1 = QWidget()
+ grid_ui = QGridLayout(tab1)
grid_ui.setColumnStretch(0,1)
- tabs.addTab(tab2, _('Display') )
-
- tab = QWidget()
- grid_wallet = QGridLayout(tab)
- grid_wallet.setColumnStretch(0,1)
- tabs.addTab(tab, _('Wallet') )
-
- fee_label = QLabel(_('Transaction fee'))
- grid_wallet.addWidget(fee_label, 2, 0)
- fee_e = QLineEdit()
- fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
- grid_wallet.addWidget(fee_e, 2, 1)
- msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
- + _('Recommended value') + ': 0.001'
- grid_wallet.addWidget(HelpButton(msg), 2, 2)
- fee_e.textChanged.connect(lambda: numbify(fee_e,False))
- if not self.config.is_modifiable('fee'):
- for w in [fee_e, fee_label]: w.setEnabled(False)
+ tabs.addTab(tab1, _('Display') )
nz_label = QLabel(_('Display zeros'))
grid_ui.addWidget(nz_label, 3, 0)
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)
-
-
- usechange_label = QLabel(_('Use change addresses'))
- grid_wallet.addWidget(usechange_label, 5, 0)
- usechange_combo = QComboBox()
- usechange_combo.addItems(['Yes', 'No'])
- usechange_combo.setCurrentIndex(not self.wallet.use_change)
- grid_wallet.addWidget(usechange_combo, 5, 1)
- grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
- if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False)
-
- gap_label = QLabel(_('Gap limit'))
- grid_wallet.addWidget(gap_label, 6, 0)
- gap_e = QLineEdit()
- gap_e.setText("%d"% self.wallet.gap_limit)
- grid_wallet.addWidget(gap_e, 6, 1)
- 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), 6, 2)
- 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)
gui_label=QLabel(_('Default GUI') + ':')
grid_ui.addWidget(gui_label , 7, 0)
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 , 9, 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, 9, 1)
+ grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes. ')), 9, 2)
+
view_label=QLabel(_('Receive Tab') + ':')
- grid_ui.addWidget(view_label , 9, 0)
+ grid_ui.addWidget(view_label , 10, 0)
view_combo = QComboBox()
view_combo.addItems([_('Simple'), _('Advanced'), _('Point of Sale')])
view_combo.setCurrentIndex(self.receive_tab_mode)
- grid_ui.addWidget(view_combo, 9, 1)
+ grid_ui.addWidget(view_combo, 10, 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' \
+ _('Point of Sale') + ': ' + _('Show QR code window and amounts requested for each address. Add menu item to request amount.') + '\n\n'
- grid_ui.addWidget(HelpButton(hh), 9, 2)
+ grid_ui.addWidget(HelpButton(hh), 10, 2)
+
+ # 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, 1)
+ msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
+ + _('Recommended value') + ': 0.001'
+ grid_wallet.addWidget(HelpButton(msg), 0, 2)
+ fee_e.textChanged.connect(lambda: numbify(fee_e,False))
+ if not self.config.is_modifiable('fee'):
+ 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, 1)
+ grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 1, 2)
+ 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, 1)
+ 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, 2)
+ 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)
+
+
+ # wallet 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 key')), 3, 0)
+ grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2)
+ grid_io.addWidget(HelpButton('Import private key'), 3, 3)
+
+ grid_io.setRowStretch(4,1)
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
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()
if need_restart:
QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK'))