from electrum.i18n import _, set_language
from electrum.util import print_error, print_msg
import os.path, json, ast, traceback
+import webbrowser
import shutil
import StringIO
default_column_widths = { "history":[40,140,350,140], "contacts":[350,330], "receive": [370,200,130] }
class ElectrumWindow(QMainWindow):
- def build_menu(self):
- m = QMenu()
- m.addAction(_("Show/Hide"), self.show_or_hide)
- m.addSeparator()
- m.addAction(_("Exit Electrum"), self.close)
- self.tray.setContextMenu(m)
-
- def show_or_hide(self):
- self.tray_activated(QSystemTrayIcon.DoubleClick)
-
- def tray_activated(self, reason):
- if reason == QSystemTrayIcon.DoubleClick:
- if self.isMinimized() or self.isHidden():
- self.show()
- else:
- self.hide()
- def __init__(self, config, network):
+
+
+ def __init__(self, config, network, gui_object):
QMainWindow.__init__(self)
self.config = config
self.network = network
-
- self._close_electrum = False
+ self.tray = gui_object.tray
+ self.go_lite = gui_object.go_lite
self.lite = None
- if sys.platform == 'darwin':
- self.icon = QIcon(":icons/electrum_dark_icon.png")
- #self.icon = QIcon(":icons/lock.png")
- else:
- self.icon = QIcon(':icons/electrum_light_icon.png')
-
- self.tray = QSystemTrayIcon(self.icon, self)
- self.tray.setToolTip('Electrum')
- self.tray.activated.connect(self.tray_activated)
-
- self.build_menu()
- self.tray.show()
self.create_status_bar()
-
self.need_update = threading.Event()
- self.decimal_point = config.get('decimal_point', 8)
+ self.decimal_point = config.get('decimal_point', 5)
self.num_zeros = int(config.get('num_zeros',0))
set_language(config.get('language'))
self.connect(self, QtCore.SIGNAL('update_status'), self.update_status)
self.connect(self, QtCore.SIGNAL('banner_signal'), lambda: self.console.showMessage(self.network.banner) )
self.connect(self, QtCore.SIGNAL('transaction_signal'), lambda: self.notify_transactions() )
+ self.connect(self, QtCore.SIGNAL('send_tx2'), self.send_tx2)
+ self.connect(self, QtCore.SIGNAL('send_tx3'), self.send_tx3)
self.history_list.setFocus(True)
self.console.showMessage(self.network.banner)
self.wallet = None
- self.init_lite()
-
-
- def go_full(self):
- self.config.set_key('lite_mode', False, True)
- self.mini.hide()
- self.show()
-
- def go_lite(self):
- self.config.set_key('lite_mode', True, True)
- self.hide()
- self.mini.show()
-
-
- def init_lite(self):
- import lite_window
- if not self.check_qt_version():
- if self.config.get('lite_mode') is True:
- msg = "Electrum was unable to load the 'Lite GUI' because it needs Qt version >= 4.7.\nChanging your config to use the 'Classic' GUI"
- QMessageBox.warning(None, "Could not start Lite GUI.", msg)
- self.config.set_key('lite_mode', False, True)
- sys.exit(0)
- self.mini = None
- self.show()
- return
-
- actuator = lite_window.MiniActuator(self)
-
- # Should probably not modify the current path but instead
- # change the behaviour of rsrc(...)
- old_path = QDir.currentPath()
- actuator.load_theme()
-
- self.mini = lite_window.MiniWindow(actuator, self.go_full, self.config)
-
- driver = lite_window.MiniDriver(self, self.mini)
-
- # Reset path back to original value now that loading the GUI
- # is completed.
- QDir.setCurrent(old_path)
-
- if self.config.get('lite_mode') is True:
- self.go_lite()
- else:
- self.go_full()
-
-
- def check_qt_version(self):
- qtVersion = qVersion()
- return int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7
def update_account_selector(self):
# Once GUI has been initialized check if we want to announce something since the callback has been called before the GUI was initialized
self.notify_transactions()
self.update_account_selector()
- self.new_account.setEnabled(self.wallet.seed_version>4)
+ # update menus
+ self.new_account_menu.setEnabled(self.wallet.can_create_accounts())
+ self.private_keys_menu.setEnabled(not self.wallet.is_watching_only())
+ self.password_menu.setEnabled(not self.wallet.is_watching_only())
+ self.seed_menu.setEnabled(self.wallet.has_seed())
+ self.mpk_menu.setEnabled(self.wallet.is_deterministic())
+
self.update_lock_icon()
self.update_buttons_on_seed()
self.update_console()
menubar = QMenuBar()
file_menu = menubar.addMenu(_("&File"))
- open_wallet_action = file_menu.addAction(_("&Open"))
- open_wallet_action.setShortcut(QKeySequence.Open)
- open_wallet_action.triggered.connect(self.open_wallet)
-
- new_wallet_action = file_menu.addAction(_("&New/Restore"))
- new_wallet_action.setShortcut(QKeySequence.New)
- new_wallet_action.triggered.connect(self.new_wallet)
-
- wallet_backup = file_menu.addAction(_("&Save Copy"))
- wallet_backup.setShortcut(QKeySequence.SaveAs)
- wallet_backup.triggered.connect(self.backup_wallet)
-
- quit_item = file_menu.addAction(_("&Quit"))
- #quit_item.setShortcut(QKeySequence.Quit)
- quit_item.triggered.connect(self.close)
+ file_menu.addAction(_("&Open"), self.open_wallet).setShortcut(QKeySequence.Open)
+ file_menu.addAction(_("&New/Restore"), self.new_wallet).setShortcut(QKeySequence.New)
+ file_menu.addAction(_("&Save Copy"), self.backup_wallet).setShortcut(QKeySequence.SaveAs)
+ file_menu.addAction(_("&Quit"), self.close)
wallet_menu = menubar.addMenu(_("&Wallet"))
-
- new_contact = wallet_menu.addAction(_("&New contact"))
- new_contact.triggered.connect(self.new_contact_dialog)
-
- self.new_account = wallet_menu.addAction(_("&New account"))
- self.new_account.triggered.connect(self.new_account_dialog)
+ wallet_menu.addAction(_("&New contact"), self.new_contact_dialog)
+ self.new_account_menu = wallet_menu.addAction(_("&New account"), self.new_account_dialog)
wallet_menu.addSeparator()
- pw = wallet_menu.addAction(_("&Password"))
- pw.triggered.connect(self.change_password_dialog)
-
- show_seed = wallet_menu.addAction(_("&Seed"))
- show_seed.triggered.connect(self.show_seed_dialog)
-
- show_mpk = wallet_menu.addAction(_("&Master Public Key"))
- show_mpk.triggered.connect(self.show_master_public_key)
+ self.password_menu = wallet_menu.addAction(_("&Password"), self.change_password_dialog)
+ self.seed_menu = wallet_menu.addAction(_("&Seed"), self.show_seed_dialog)
+ self.mpk_menu = wallet_menu.addAction(_("&Master Public Keys"), self.show_master_public_keys)
wallet_menu.addSeparator()
-
labels_menu = wallet_menu.addMenu(_("&Labels"))
- import_labels = labels_menu.addAction(_("&Import"))
- import_labels.triggered.connect(self.do_import_labels)
- export_labels = labels_menu.addAction(_("&Export"))
- export_labels.triggered.connect(self.do_export_labels)
-
- keys_menu = wallet_menu.addMenu(_("&Private keys"))
- import_keys = keys_menu.addAction(_("&Import"))
- import_keys.triggered.connect(self.do_import_privkey)
- export_keys = keys_menu.addAction(_("&Export"))
- export_keys.triggered.connect(self.do_export_privkeys)
-
- ex_history = wallet_menu.addAction(_("&Export History"))
- ex_history.triggered.connect(self.do_export_history)
+ labels_menu.addAction(_("&Import"), self.do_import_labels)
+ labels_menu.addAction(_("&Export"), self.do_export_labels)
+ self.private_keys_menu = wallet_menu.addMenu(_("&Private keys"))
+ self.private_keys_menu.addAction(_("&Import"), self.do_import_privkey)
+ self.private_keys_menu.addAction(_("&Export"), self.do_export_privkeys)
+ wallet_menu.addAction(_("&Export History"), self.do_export_history)
tools_menu = menubar.addMenu(_("&Tools"))
# Settings / Preferences are all reserved keywords in OSX using this as work around
- preferences_name = _("Electrum preferences") if sys.platform == 'darwin' else _("Preferences")
- preferences_menu = tools_menu.addAction(preferences_name)
- #preferences_menu.setShortcut(QKeySequence.Preferences)
- preferences_menu.triggered.connect(self.settings_dialog)
-
- network = tools_menu.addAction(_("&Network"))
- network.triggered.connect(self.run_network_dialog)
-
- plugins_labels = tools_menu.addAction(_("&Plugins"))
- plugins_labels.triggered.connect(self.plugins_dialog)
-
+ tools_menu.addAction(_("Electrum preferences") if sys.platform == 'darwin' else _("Preferences"), self.settings_dialog)
+ tools_menu.addAction(_("&Network"), self.run_network_dialog)
+ tools_menu.addAction(_("&Plugins"), self.plugins_dialog)
+ tools_menu.addSeparator()
+ tools_menu.addAction(_("&Sign/verify message"), self.sign_verify_message)
+ #tools_menu.addAction(_("&Encrypt/decrypt message"), self.encrypt_message)
tools_menu.addSeparator()
csv_transaction_menu = tools_menu.addMenu(_("&Create transaction"))
-
- csv_transaction_file = csv_transaction_menu.addAction(_("&From CSV file"))
- csv_transaction_file.triggered.connect(self.do_process_from_csv_file)
-
- csv_transaction_text = csv_transaction_menu.addAction(_("&From CSV text"))
- csv_transaction_text.triggered.connect(self.do_process_from_csv_text)
+ csv_transaction_menu.addAction(_("&From CSV file"), self.do_process_from_csv_file)
+ csv_transaction_menu.addAction(_("&From CSV text"), self.do_process_from_csv_text)
raw_transaction_menu = tools_menu.addMenu(_("&Load transaction"))
-
- raw_transaction_file = raw_transaction_menu.addAction(_("&From file"))
- raw_transaction_file.triggered.connect(self.do_process_from_file)
-
- raw_transaction_text = raw_transaction_menu.addAction(_("&From text"))
- raw_transaction_text.triggered.connect(self.do_process_from_text)
-
- raw_transaction_text = raw_transaction_menu.addAction(_("&From the blockchain"))
- raw_transaction_text.triggered.connect(self.do_process_from_txid)
-
+ raw_transaction_menu.addAction(_("&From file"), self.do_process_from_file)
+ raw_transaction_menu.addAction(_("&From text"), self.do_process_from_text)
+ raw_transaction_menu.addAction(_("&From the blockchain"), self.do_process_from_txid)
help_menu = menubar.addMenu(_("&Help"))
- show_about = help_menu.addAction(_("&About"))
- show_about.triggered.connect(self.show_about)
- web_open = help_menu.addAction(_("&Official website"))
- web_open.triggered.connect(lambda: webbrowser.open("http://electrum.org"))
-
+ help_menu.addAction(_("&About"), self.show_about)
+ help_menu.addAction(_("&Official website"), lambda: webbrowser.open("http://electrum.org"))
help_menu.addSeparator()
- doc_open = help_menu.addAction(_("&Documentation"))
- doc_open.setShortcut(QKeySequence.HelpContents)
- doc_open.triggered.connect(lambda: webbrowser.open("http://electrum.org/documentation.html"))
- report_bug = help_menu.addAction(_("&Report Bug"))
- report_bug.triggered.connect(self.show_report_bug)
+ help_menu.addAction(_("&Documentation"), lambda: webbrowser.open("http://electrum.org/documentation.html")).setShortcut(QKeySequence.HelpContents)
+ help_menu.addAction(_("&Report Bug"), self.show_report_bug)
self.setMenuBar(menubar)
return
print_error("Notifying GUI")
- if len(self.network.interface.pending_transactions_for_notifications) > 0:
+ if len(self.network.pending_transactions_for_notifications) > 0:
# Combine the transactions if there are more then three
- tx_amount = len(self.network.interface.pending_transactions_for_notifications)
+ tx_amount = len(self.network.pending_transactions_for_notifications)
if(tx_amount >= 3):
total_amount = 0
- for tx in self.network.interface.pending_transactions_for_notifications:
+ for tx in self.network.pending_transactions_for_notifications:
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
if(v > 0):
total_amount += v
self.notify(_("%(txs)s new transactions received. Total amount received in the new transactions %(amount)s %(unit)s") \
% { 'txs' : tx_amount, 'amount' : self.format_amount(total_amount), 'unit' : self.base_unit()})
- self.network.interface.pending_transactions_for_notifications = []
+ self.network.pending_transactions_for_notifications = []
else:
- for tx in self.network.interface.pending_transactions_for_notifications:
+ for tx in self.network.pending_transactions_for_notifications:
if tx:
- self.network.interface.pending_transactions_for_notifications.remove(tx)
+ self.network.pending_transactions_for_notifications.remove(tx)
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
if(v > 0):
self.notify(_("New transaction received. %(amount)s %(unit)s") % { 'amount' : self.format_amount(v), 'unit' : self.base_unit()})
text = _( "Balance" ) + ": %s "%( self.format_amount(c) ) + self.base_unit()
if u: text += " [%s unconfirmed]"%( self.format_amount(u,True).strip() )
+ # append fiat balance and price from exchange rate plugin
r = {}
- run_hook('set_quote_text', c+u, r)
+ run_hook('get_fiat_status_text', c+u, r)
quote = r.get(0)
if quote:
- text += " (%s)"%quote
+ text += "%s"%quote
self.tray.setToolTip(text)
icon = QIcon(":icons/status_connected.png")
def create_history_menu(self, position):
self.history_list.selectedIndexes()
item = self.history_list.currentItem()
+ be = self.config.get('block_explorer', 'Blockchain.info')
+ if be == 'Blockchain.info':
+ block_explorer = 'https://blockchain.info/tx/'
+ elif be == 'Blockr.io':
+ block_explorer = 'https://blockr.io/tx/info/'
+ elif be == 'Insight.is':
+ block_explorer = 'http://live.insight.is/tx/'
if not item: return
tx_hash = str(item.data(0, Qt.UserRole).toString())
if not tx_hash: return
menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
menu.addAction(_("Details"), lambda: self.show_transaction(self.wallet.transactions.get(tx_hash)))
menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
+ menu.addAction(_("View on block explorer"), lambda: webbrowser.open(block_explorer + tx_hash))
menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
+ run_hook('history_tab_update')
def create_send_tab(self):
+ _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
+ _('A suggested fee is automatically added to this field. You may override it. The suggested fee increases with the size of the transaction.')), 5, 3)
+ run_hook('exchange_rate_button', grid)
self.send_button = EnterButton(_("Send"), self.do_send)
grid.addWidget(self.send_button, 6, 1)
if self.amount_e.is_shortcut:
self.amount_e.is_shortcut = False
sendable = self.get_sendable_balance()
- inputs, total, fee = self.wallet.choose_tx_inputs( sendable, 0, self.get_payment_sources())
- fee = self.wallet.estimated_fee(inputs)
+ # there is only one output because we are completely spending inputs
+ inputs, total, fee = self.wallet.choose_tx_inputs( sendable, 0, 1, self.get_payment_sources())
+ fee = self.wallet.estimated_fee(inputs, 1)
amount = total - fee
self.amount_e.setText( self.format_amount(amount) )
self.fee_e.setText( self.format_amount( fee ) )
if not is_fee: fee = None
if amount is None:
return
- inputs, total, fee = self.wallet.choose_tx_inputs(amount, fee, self.get_payment_sources())
+ # assume that there will be 2 outputs (one for change)
+ inputs, total, fee = self.wallet.choose_tx_inputs(amount, fee, 2, self.get_payment_sources())
if not is_fee:
self.fee_e.setText( self.format_amount( fee ) )
if inputs:
self.funds_error = True
text = _( "Not enough funds" )
c, u = self.wallet.get_frozen_balance()
- if c+u: text += ' (' + self.format_amount(c+u).strip() + self.base_unit() + ' ' +_("are frozen") + ')'
+ if c+u: text += ' (' + self.format_amount(c+u).strip() + ' ' + self.base_unit() + ' ' +_("are frozen") + ')'
self.statusBar().showMessage(text)
self.amount_e.setPalette(palette)
self.send_tx(to_address, amount, fee, label)
+ def waiting_dialog(self, message):
+ d = QDialog(self)
+ d.setWindowTitle('Please wait')
+ l = QLabel(message)
+ vbox = QVBoxLayout(d)
+ vbox.addWidget(l)
+ d.show()
+ return d
+
+
@protected
def send_tx(self, to_address, amount, fee, label, password):
+
+ # first, create an unsigned tx
+ domain = self.get_payment_sources()
+ outputs = [(to_address, amount)]
try:
- tx = self.wallet.mktx( [(to_address, amount)], password, fee,
- domain=self.get_payment_sources())
+ tx = self.wallet.make_unsigned_transaction(outputs, fee, None, domain)
+ tx.error = None
except Exception as e:
traceback.print_exc(file=sys.stdout)
self.show_message(str(e))
return
+ # call hook to see if plugin needs gui interaction
+ run_hook('send_tx', tx)
+
+ # sign the tx
+ def sign_thread():
+ time.sleep(0.1)
+ keypairs = {}
+ self.wallet.add_keypairs_from_wallet(tx, keypairs, password)
+ self.wallet.sign_transaction(tx, keypairs, password)
+ self.signed_tx_data = (tx, fee, label)
+ self.emit(SIGNAL('send_tx2'))
+ self.tx_wait_dialog = self.waiting_dialog('Signing..')
+ threading.Thread(target=sign_thread).start()
+
+ # add recipient to addressbook
+ if to_address not in self.wallet.addressbook and not self.wallet.is_mine(to_address):
+ self.wallet.addressbook.append(to_address)
+
+
+ def send_tx2(self):
+ tx, fee, label = self.signed_tx_data
+ self.tx_wait_dialog.accept()
+
+ if tx.error:
+ self.show_message(tx.error)
+ return
+
if tx.requires_fee(self.wallet.verifier) and fee < MIN_RELAY_TX_FEE:
QMessageBox.warning(self, _('Error'), _("This transaction requires a higher fee, or it will not be propagated by the network."), _('OK'))
return
if label:
self.wallet.set_label(tx.hash(), label)
- if tx.is_complete:
- h = self.wallet.send_tx(tx)
- waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait..."))
- status, msg = self.wallet.receive_tx( h, tx )
- if status:
- QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
- self.do_clear()
- self.update_contacts_tab()
- else:
- QMessageBox.warning(self, _('Error'), msg, _('OK'))
- else:
-
+ if not tx.is_complete() or self.config.get('show_before_broadcast'):
self.show_transaction(tx)
+ return
+
+ # broadcast the tx
+ def broadcast_thread():
+ self.tx_broadcast_result = self.wallet.sendtx(tx)
+ self.emit(SIGNAL('send_tx3'))
+ self.tx_broadcast_dialog = self.waiting_dialog('Broadcasting..')
+ threading.Thread(target=broadcast_thread).start()
+
+
+ def send_tx3(self):
+ self.tx_broadcast_dialog.accept()
+ status, msg = self.tx_broadcast_result
+ if status:
+ QMessageBox.information(self, '', _('Payment sent.') + '\n' + msg, _('OK'))
+ self.do_clear()
+ self.update_contacts_tab()
+ else:
+ QMessageBox.warning(self, _('Error'), msg, _('OK'))
- # add recipient to addressbook
- if to_address not in self.wallet.addressbook and not self.wallet.is_mine(to_address):
- self.wallet.addressbook.append(to_address)
- def set_url(self, url):
- address, amount, label, message, signature, identity, url = util.parse_url(url)
- try:
- if amount and self.base_unit() == 'mBTC': amount = str( 1000* Decimal(amount))
- elif amount: amount = str(Decimal(amount))
- except Exception:
- amount = "0.0"
- QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
- if self.mini:
- self.mini.set_payment_fields(address, amount)
+ def set_send(self, address, amount, label, message):
if label and self.wallet.labels.get(address) != label:
if self.question('Give label "%s" to address %s ?'%(label,address)):
self.wallet.addressbook.append(address)
self.wallet.set_label(address, label)
- run_hook('set_url', url, self.show_message, self.question)
-
self.tabs.setCurrentIndex(1)
label = self.wallet.labels.get(address)
m_addr = label + ' <'+ address +'>' if label else address
if amount:
self.amount_e.setText(amount)
- if identity:
- self.set_frozen(self.payto_e,True)
- self.set_frozen(self.amount_e,True)
- self.set_frozen(self.message_e,True)
- self.payto_sig.setText( ' '+_('The bitcoin URI was signed by')+' ' + identity )
- else:
- self.payto_sig.setVisible(False)
def do_clear(self):
self.payto_sig.setVisible(False)
else:
menu.addAction(_("Maximize"), lambda: self.account_set_expanded(item, k, True))
menu.addAction(_("Rename"), lambda: self.edit_account_label(k))
- menu.addAction(_("View details"), lambda: self.show_account_details(k))
+ if self.wallet.seed_version > 4:
+ menu.addAction(_("View details"), lambda: self.show_account_details(k))
if self.wallet.account_is_pending(k):
menu.addAction(_("Delete"), lambda: self.delete_pending_account(k))
menu.exec_(self.receive_list.viewport().mapToGlobal(position))
menu.addAction(_("Copy to clipboard"), lambda: self.app.clipboard().setText(addr))
menu.addAction(_("QR code"), lambda: self.show_qrcode("bitcoin:" + addr, _("Address")) )
menu.addAction(_("Edit label"), lambda: self.edit_label(True))
- if self.wallet.seed:
+ menu.addAction(_("Public keys"), lambda: self.show_public_keys(addr))
+ if not self.wallet.is_watching_only():
menu.addAction(_("Private key"), lambda: self.show_private_key(addr))
- menu.addAction(_("Sign message"), lambda: self.sign_message(addr))
+ menu.addAction(_("Sign/verify message"), lambda: self.sign_verify_message(addr))
+ #menu.addAction(_("Encrypt/decrypt message"), lambda: self.encrypt_message(addr))
if addr in self.wallet.imported_keys:
menu.addAction(_("Remove from wallet"), lambda: self.delete_imported_key(addr))
l.setColumnWidth(i, width)
if self.current_account is None:
- account_items = self.wallet.accounts.items()
+ account_items = sorted(self.wallet.accounts.items())
elif self.current_account != -1:
account_items = [(self.current_account, self.wallet.accounts.get(self.current_account))]
else:
account_items = []
+ pending_accounts = self.wallet.get_pending_accounts()
+
for k, account in account_items:
- name = self.wallet.get_account_name(k)
- c,u = self.wallet.get_account_balance(k)
- account_item = QTreeWidgetItem( [ name, '', self.format_amount(c+u), ''] )
- l.addTopLevelItem(account_item)
- account_item.setExpanded(self.accounts_expanded.get(k, True))
- account_item.setData(0, 32, k)
- if not self.wallet.is_seeded(k):
- icon = QIcon(":icons/key.png")
- account_item.setIcon(0, icon)
+ if len(account_items) + len(pending_accounts) > 1:
+ name = self.wallet.get_account_name(k)
+ c,u = self.wallet.get_account_balance(k)
+ account_item = QTreeWidgetItem( [ name, '', self.format_amount(c+u), ''] )
+ l.addTopLevelItem(account_item)
+ account_item.setExpanded(self.accounts_expanded.get(k, True))
+ account_item.setData(0, 32, k)
+ else:
+ account_item = None
for is_change in ([0,1]):
name = _("Receiving") if not is_change else _("Change")
seq_item = QTreeWidgetItem( [ name, '', '', '', ''] )
- account_item.addChild(seq_item)
+ if account_item:
+ account_item.addChild(seq_item)
+ else:
+ l.addTopLevelItem(seq_item)
+
used_item = QTreeWidgetItem( [ _("Used"), '', '', '', ''] )
used_flag = False
if not is_change: seq_item.setExpanded(True)
seq_item.addChild(item)
- for k, addr in self.wallet.get_pending_accounts():
+ for k, addr in pending_accounts:
name = self.wallet.labels.get(k,'')
account_item = QTreeWidgetItem( [ name + " [ "+_('pending account')+" ]", '', '', ''] )
self.update_receive_item(item)
self.updatelabel = UpdateLabel(self.config, sb)
self.account_selector = QComboBox()
+ self.account_selector.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.connect(self.account_selector,SIGNAL("activated(QString)"),self.change_account)
sb.addPermanentWidget(self.account_selector)
def update_buttons_on_seed(self):
- if not self.wallet.is_watching_only():
+ if self.wallet.has_seed():
self.seed_button.show()
+ else:
+ self.seed_button.hide()
+
+ if not self.wallet.is_watching_only():
self.password_button.show()
self.send_button.setText(_("Send"))
else:
self.password_button.hide()
- self.seed_button.hide()
self.send_button.setText(_("Create unsigned transaction"))
def new_contact_dialog(self):
d = QDialog(self)
+ d.setWindowTitle(_("New Contact"))
vbox = QVBoxLayout(d)
vbox.addWidget(QLabel(_('New Contact')+':'))
self.tabs.setCurrentIndex(3)
- def new_account_dialog(self):
+ @protected
+ def new_account_dialog(self, password):
dialog = QDialog(self)
dialog.setModal(1)
name = str(e.text())
if not name: return
- self.wallet.create_pending_account('1', name)
+ self.wallet.create_pending_account(name, password)
self.update_receive_tab()
self.tabs.setCurrentIndex(2)
- def show_master_public_key_old(self):
- dialog = QDialog(self)
- dialog.setModal(1)
- dialog.setWindowTitle(_("Master Public Key"))
-
- main_text = QTextEdit()
- main_text.setText(self.wallet.get_master_public_key())
- main_text.setReadOnly(True)
- main_text.setMaximumHeight(170)
- qrw = QRCodeWidget(self.wallet.get_master_public_key())
-
- ok_button = QPushButton(_("OK"))
- ok_button.setDefault(True)
- ok_button.clicked.connect(dialog.accept)
-
- main_layout = QGridLayout()
- main_layout.addWidget(QLabel(_('Your Master Public Key is:')), 0, 0, 1, 2)
-
- main_layout.addWidget(main_text, 1, 0)
- main_layout.addWidget(qrw, 1, 1 )
-
- vbox = QVBoxLayout()
- vbox.addLayout(main_layout)
- vbox.addLayout(close_button(dialog))
- dialog.setLayout(vbox)
- dialog.exec_()
-
- def show_master_public_key(self):
-
- if self.wallet.seed_version == 4:
- self.show_master_public_key_old()
- return
+ def show_master_public_keys(self):
dialog = QDialog(self)
dialog.setModal(1)
dialog.setWindowTitle(_("Master Public Keys"))
- chain_text = QTextEdit()
- chain_text.setReadOnly(True)
- chain_text.setMaximumHeight(170)
- chain_qrw = QRCodeWidget()
-
- mpk_text = QTextEdit()
- mpk_text.setReadOnly(True)
- mpk_text.setMaximumHeight(170)
- mpk_qrw = QRCodeWidget()
-
main_layout = QGridLayout()
-
- main_layout.addWidget(QLabel(_('Key')), 1, 0)
- main_layout.addWidget(mpk_text, 1, 1)
- main_layout.addWidget(mpk_qrw, 1, 2)
-
- main_layout.addWidget(QLabel(_('Chain')), 2, 0)
- main_layout.addWidget(chain_text, 2, 1)
- main_layout.addWidget(chain_qrw, 2, 2)
-
- def update(key):
- c, K, cK = self.wallet.master_public_keys[str(key)]
- chain_text.setText(c)
- chain_qrw.set_addr(c)
- chain_qrw.update_qr()
- mpk_text.setText(K)
- mpk_qrw.set_addr(K)
- mpk_qrw.update_qr()
-
- key_selector = QComboBox()
- keys = sorted(self.wallet.master_public_keys.keys())
- key_selector.addItems(keys)
-
- main_layout.addWidget(QLabel(_('Derivation:')), 0, 0)
- main_layout.addWidget(key_selector, 0, 1)
- dialog.connect(key_selector,SIGNAL("activated(QString)"),update)
-
- update(keys[0])
+ mpk_dict = self.wallet.get_master_public_keys()
+ i = 0
+ for key, value in mpk_dict.items():
+ main_layout.addWidget(QLabel(key), i, 0)
+ mpk_text = QTextEdit()
+ mpk_text.setReadOnly(True)
+ mpk_text.setMaximumHeight(170)
+ mpk_text.setText(value)
+ main_layout.addWidget(mpk_text, i + 1, 0)
+ i += 2
vbox = QVBoxLayout()
vbox.addLayout(main_layout)
@protected
def show_seed_dialog(self, password):
- if self.wallet.is_watching_only():
- QMessageBox.information(self, _('Message'), _('This is a watching-only wallet'), _('OK'))
+ if not self.wallet.has_seed():
+ QMessageBox.information(self, _('Message'), _('This wallet has no seed'), _('OK'))
return
- if self.wallet.seed:
- try:
- mnemonic = self.wallet.get_mnemonic(password)
- except Exception:
- QMessageBox.warning(self, _('Error'), _('Incorrect Password'), _('OK'))
- return
- from seed_dialog import SeedDialog
- d = SeedDialog(self, mnemonic, self.wallet.imported_keys)
- d.exec_()
- else:
- l = {}
- for k in self.wallet.master_private_keys.keys():
- pk = self.wallet.get_master_private_key(k, password)
- l[k] = pk
- from seed_dialog import PrivateKeysDialog
- d = PrivateKeysDialog(self,l)
- d.exec_()
-
-
+ try:
+ mnemonic = self.wallet.get_mnemonic(password)
+ except Exception:
+ QMessageBox.warning(self, _('Error'), _('Incorrect Password'), _('OK'))
+ return
+ from seed_dialog import SeedDialog
+ d = SeedDialog(self, mnemonic, self.wallet.imported_keys)
+ d.exec_()
apply( func, args)
+ def show_public_keys(self, address):
+ if not address: return
+ try:
+ pubkey_list = self.wallet.get_public_keys(address)
+ except Exception as e:
+ traceback.print_exc(file=sys.stdout)
+ self.show_message(str(e))
+ return
+
+ d = QDialog(self)
+ d.setMinimumSize(600, 200)
+ d.setModal(1)
+ vbox = QVBoxLayout()
+ vbox.addWidget( QLabel(_("Address") + ': ' + address))
+ vbox.addWidget( QLabel(_("Public key") + ':'))
+ keys = QTextEdit()
+ keys.setReadOnly(True)
+ keys.setText('\n'.join(pubkey_list))
+ vbox.addWidget(keys)
+ #vbox.addWidget( QRCodeWidget('\n'.join(pk_list)) )
+ vbox.addLayout(close_button(d))
+ d.setLayout(vbox)
+ d.exec_()
+
@protected
def show_private_key(self, address, password):
if not address: return
try:
pk_list = self.wallet.get_private_key(address, password)
except Exception as e:
+ traceback.print_exc(file=sys.stdout)
self.show_message(str(e))
return
keys.setReadOnly(True)
keys.setText('\n'.join(pk_list))
vbox.addWidget(keys)
+ vbox.addWidget( QRCodeWidget('\n'.join(pk_list)) )
vbox.addLayout(close_button(d))
d.setLayout(vbox)
d.exec_()
except Exception as e:
self.show_message(str(e))
- def sign_message(self, address):
- if not address: return
+ def do_verify(self, address, message, signature):
+ message = unicode(message.toPlainText())
+ message = message.encode('utf-8')
+ if bitcoin.verify_message(address.text(), str(signature.toPlainText()), message):
+ self.show_message(_("Signature verified"))
+ else:
+ self.show_message(_("Error: wrong signature"))
+
+
+ def sign_verify_message(self, address=''):
d = QDialog(self)
d.setModal(1)
- d.setWindowTitle(_('Sign Message'))
+ d.setWindowTitle(_('Sign/verify 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)
+ layout = QGridLayout(d)
- sign_message = QTextEdit()
- layout.addWidget(QLabel(_('Message')), 2, 0)
- layout.addWidget(sign_message, 2, 1)
+ message_e = QTextEdit()
+ layout.addWidget(QLabel(_('Message')), 1, 0)
+ layout.addWidget(message_e, 1, 1)
layout.setRowStretch(2,3)
- sign_signature = QTextEdit()
+ address_e = QLineEdit()
+ address_e.setText(address)
+ layout.addWidget(QLabel(_('Address')), 2, 0)
+ layout.addWidget(address_e, 2, 1)
+
+ signature_e = QTextEdit()
layout.addWidget(QLabel(_('Signature')), 3, 0)
- layout.addWidget(sign_signature, 3, 1)
+ layout.addWidget(signature_e, 3, 1)
layout.setRowStretch(3,1)
-
hbox = QHBoxLayout()
+
b = QPushButton(_("Sign"))
+ b.clicked.connect(lambda: self.do_sign(address_e, message_e, signature_e))
hbox.addWidget(b)
- b.clicked.connect(lambda: self.do_sign(sign_address, sign_message, sign_signature))
+
+ b = QPushButton(_("Verify"))
+ b.clicked.connect(lambda: self.do_verify(address_e, message_e, signature_e))
+ hbox.addWidget(b)
+
b = QPushButton(_("Close"))
b.clicked.connect(d.accept)
hbox.addWidget(b)
layout.addLayout(hbox, 4, 1)
- tab_widget.addTab(tab, _("Sign"))
+ d.exec_()
+
+ @protected
+ def do_decrypt(self, message_e, pubkey_e, encrypted_e, password):
+ try:
+ decrypted = self.wallet.decrypt_message(str(pubkey_e.text()), str(encrypted_e.toPlainText()), password)
+ message_e.setText(decrypted)
+ except Exception as e:
+ self.show_message(str(e))
- tab = QWidget()
- layout = QGridLayout(tab)
- verify_address = QLineEdit()
- layout.addWidget(QLabel(_('Address')), 1, 0)
- layout.addWidget(verify_address, 1, 1)
+ def do_encrypt(self, message_e, pubkey_e, encrypted_e):
+ message = unicode(message_e.toPlainText())
+ message = message.encode('utf-8')
+ try:
+ encrypted = bitcoin.encrypt_message(message, str(pubkey_e.text()))
+ encrypted_e.setText(encrypted)
+ except Exception as e:
+ self.show_message(str(e))
+
+
+
+ def encrypt_message(self, address = ''):
+ d = QDialog(self)
+ d.setModal(1)
+ d.setWindowTitle(_('Encrypt/decrypt Message'))
+ d.setMinimumSize(610, 490)
+
+ layout = QGridLayout(d)
- verify_message = QTextEdit()
- layout.addWidget(QLabel(_('Message')), 2, 0)
- layout.addWidget(verify_message, 2, 1)
+ message_e = QTextEdit()
+ layout.addWidget(QLabel(_('Message')), 1, 0)
+ layout.addWidget(message_e, 1, 1)
layout.setRowStretch(2,3)
- verify_signature = QTextEdit()
- layout.addWidget(QLabel(_('Signature')), 3, 0)
- layout.addWidget(verify_signature, 3, 1)
- layout.setRowStretch(3,1)
+ pubkey_e = QLineEdit()
+ if address:
+ pubkey = self.wallet.getpubkeys(address)[0]
+ pubkey_e.setText(pubkey)
+ layout.addWidget(QLabel(_('Public key')), 2, 0)
+ layout.addWidget(pubkey_e, 2, 1)
- def do_verify():
- message = unicode(verify_message.toPlainText())
- message = message.encode('utf-8')
- if bitcoin.verify_message(verify_address.text(), str(verify_signature.toPlainText()), message):
- self.show_message(_("Signature verified"))
- else:
- self.show_message(_("Error: wrong signature"))
+ encrypted_e = QTextEdit()
+ layout.addWidget(QLabel(_('Encrypted')), 3, 0)
+ layout.addWidget(encrypted_e, 3, 1)
+ layout.setRowStretch(3,1)
hbox = QHBoxLayout()
- b = QPushButton(_("Verify"))
- b.clicked.connect(do_verify)
+ b = QPushButton(_("Encrypt"))
+ b.clicked.connect(lambda: self.do_encrypt(message_e, pubkey_e, encrypted_e))
hbox.addWidget(b)
+
+ b = QPushButton(_("Decrypt"))
+ b.clicked.connect(lambda: self.do_decrypt(message_e, pubkey_e, encrypted_e))
+ 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)
+ layout.addLayout(hbox, 4, 1)
d.exec_()
-
-
def question(self, msg):
return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
def password_dialog(self ):
d = QDialog(self)
d.setModal(1)
+ d.setWindowTitle(_("Enter Password"))
pw = QLineEdit()
pw.setEchoMode(2)
def do_export_history(self):
- from lite_window import csv_transaction
- csv_transaction(self.wallet)
+ wallet = self.wallet
+ select_export = _('Select file to export your wallet transactions to')
+ fileName = QFileDialog.getSaveFileName(QWidget(), select_export, os.path.expanduser('~/electrum-history.csv'), "*.csv")
+ if not fileName:
+ return
+
+ try:
+ with open(fileName, "w+") as csvfile:
+ transaction = csv.writer(csvfile)
+ transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
+ for item in wallet.get_tx_history():
+ tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
+ if confirmations:
+ if timestamp is not None:
+ try:
+ time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
+ except [RuntimeError, TypeError, NameError] as reason:
+ time_string = "unknown"
+ pass
+ else:
+ time_string = "unknown"
+ else:
+ time_string = "pending"
+
+ if value is not None:
+ value_string = format_satoshis(value, True)
+ else:
+ value_string = '--'
+
+ if fee is not None:
+ fee_string = format_satoshis(fee, True)
+ else:
+ fee_string = '0'
+
+ if tx_hash:
+ label, is_default_label = wallet.get_label(tx_hash)
+ label = label.encode('utf-8')
+ else:
+ label = ""
+
+ balance_string = format_satoshis(balance, False)
+ transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
+ QMessageBox.information(None,_("CSV Export created"), _("Your CSV export has been successfully created."))
+
+ except (IOError, os.error), reason:
+ export_error_label = _("Electrum was unable to produce a transaction export.")
+ QMessageBox.critical(None,_("Unable to create csv"), export_error_label + "\n" + str(reason))
+
@protected
grid.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions.')+' '), 4, 2)
if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
- grid.setRowStretch(5,1)
+ block_explorers = ['Blockchain.info', 'Blockr.io', 'Insight.is']
+ block_ex_label = QLabel(_('Online Block Explorer') + ':')
+ grid.addWidget(block_ex_label, 5, 0)
+ block_ex_combo = QComboBox()
+ block_ex_combo.addItems(block_explorers)
+ block_ex_combo.setCurrentIndex(block_explorers.index(self.config.get('block_explorer', 'Blockchain.info')))
+ grid.addWidget(block_ex_combo, 5, 1)
+ grid.addWidget(HelpButton(_('Choose which online block explorer to use for functions that open a web browser')+' '), 5, 2)
+
+ show_tx = self.config.get('show_before_broadcast', False)
+ showtx_cb = QCheckBox(_('Show before broadcast'))
+ showtx_cb.setChecked(show_tx)
+ grid.addWidget(showtx_cb, 6, 0)
+ grid.addWidget(HelpButton(_('Display the details of your transactions before broadcasting it.')), 6, 2)
vbox.addLayout(grid)
+ vbox.addStretch(1)
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
self.wallet.use_change = usechange_result
self.wallet.storage.put('use_change', self.wallet.use_change)
+ if showtx_cb.isChecked() != show_tx:
+ self.config.set_key('show_before_broadcast', not show_tx)
+
unit_result = units[unit_combo.currentIndex()]
if self.base_unit() != unit_result:
self.decimal_point = 8 if unit_result == 'BTC' else 5
self.config.set_key("language", lang_request, True)
need_restart = True
+ be_result = block_explorers[block_ex_combo.currentIndex()]
+ self.config.set_key('block_explorer', be_result, True)
+
run_hook('close_settings_dialog')
if need_restart:
event.accept()
-
def plugins_dialog(self):
from electrum.plugins import plugins
def show_account_details(self, k):
+ account = self.wallet.accounts[k]
+
d = QDialog(self)
d.setWindowTitle(_('Account Details'))
d.setModal(1)
vbox = QVBoxLayout(d)
- roots = self.wallet.get_roots(k)
-
name = self.wallet.get_account_name(k)
label = QLabel('Name: ' + name)
vbox.addWidget(label)
- acctype = '2 of 2' if len(roots) == 2 else '2 of 3' if len(roots) == 3 else 'Single key'
- vbox.addWidget(QLabel('Type: ' + acctype))
+ vbox.addWidget(QLabel(_('Address type') + ': ' + account.get_type()))
- label = QLabel('Derivation: ' + k)
- vbox.addWidget(label)
+ vbox.addWidget(QLabel(_('Derivation') + ': ' + k))
+
+ vbox.addWidget(QLabel(_('Master Public Key:')))
+
+ text = QTextEdit()
+ text.setReadOnly(True)
+ text.setMaximumHeight(170)
+ vbox.addWidget(text)
- #for root in roots:
- # mpk = self.wallet.master_public_keys[root]
- # text = QTextEdit()
- # text.setReadOnly(True)
- # text.setMaximumHeight(120)
- # text.setText(repr(mpk))
- # vbox.addWidget(text)
+ mpk_text = '\n'.join( account.get_master_pubkeys() )
+ text.setText(mpk_text)
vbox.addLayout(close_button(d))
d.exec_()