import StringIO
-try:
- import PyQt4
-except:
- sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
-
+import PyQt4
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import PyQt4.QtCore as QtCore
from electrum.bitcoin import MIN_RELAY_TX_FEE, is_valid
from electrum.plugins import run_hook
-try:
- import icons_rc
-except:
- sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o gui/icons_rc.py'")
+import icons_rc
from electrum.wallet import format_satoshis
from electrum import Transaction
-default_column_widths = { "history":[40,140,350,140], "contacts":[350,330], "receive":[[370], [370,200,130]] }
+default_column_widths = { "history":[40,140,350,140], "contacts":[350,330], "receive": [370,200,130] }
class ElectrumWindow(QMainWindow):
def changeEvent(self, event):
if reason == QSystemTrayIcon.DoubleClick:
self.showNormal()
+ def showNormal(self):
+ self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
- def __init__(self, config, network, go_lite):
+ def __init__(self, config, network):
QMainWindow.__init__(self)
self.config = config
self.network = network
- self.go_lite = go_lite
self._close_electrum = False
self.lite = None
- self.current_account = self.config.get("current_account", None)
- self.icon = QIcon(':icons/electrum.png')
+ 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.need_update = threading.Event()
- self.expert_mode = config.get('classic_expert_mode', False)
self.decimal_point = config.get('decimal_point', 8)
self.num_zeros = int(config.get('num_zeros',0))
self.completions = QStringListModel()
self.tabs = tabs = QTabWidget(self)
- self.column_widths = self.config.get("column_widths", default_column_widths )
+ self.column_widths = self.config.get("column_widths_2", default_column_widths )
tabs.addTab(self.create_history_tab(), _('History') )
tabs.addTab(self.create_send_tab(), _('Send') )
tabs.addTab(self.create_receive_tab(), _('Receive') )
g = self.config.get("winpos-qt",[100, 100, 840, 400])
self.setGeometry(g[0], g[1], g[2], g[3])
+ self.setWindowIcon(QIcon(":icons/electrum.png"))
self.init_menubar()
QShortcut(QKeySequence("Ctrl+W"), self, self.close)
self.connect(self, QtCore.SIGNAL('transaction_signal'), lambda: self.notify_transactions() )
self.history_list.setFocus(True)
-
- # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
- if platform.system() == 'Windows':
- n = 3 if self.wallet.seed else 2
- tabs.setCurrentIndex (n)
- tabs.setCurrentIndex (0)
+ # network callbacks
+ if self.network:
+ self.network.register_callback('updated', lambda: self.need_update.set())
+ self.network.register_callback('banner', lambda: self.emit(QtCore.SIGNAL('banner_signal')))
+ self.network.register_callback('disconnected', lambda: self.emit(QtCore.SIGNAL('update_status')))
+ self.network.register_callback('disconnecting', lambda: self.emit(QtCore.SIGNAL('update_status')))
+ self.network.register_callback('new_transaction', lambda: self.emit(QtCore.SIGNAL('transaction_signal')))
+
+ # set initial message
+ 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()
- def load_wallet(self, wallet):
- import electrum
- self.wallet = wallet
+ self.mini = lite_window.MiniWindow(actuator, self.go_full, self.config)
- self.network.register_callback('updated', lambda: self.need_update.set())
- self.network.register_callback('banner', lambda: self.emit(QtCore.SIGNAL('banner_signal')))
- self.network.register_callback('disconnected', lambda: self.emit(QtCore.SIGNAL('update_status')))
- self.network.register_callback('disconnecting', lambda: self.emit(QtCore.SIGNAL('update_status')))
- self.network.register_callback('new_transaction', lambda: self.emit(QtCore.SIGNAL('transaction_signal')))
- title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.wallet.storage.path
- if not self.wallet.seed: title += ' [%s]' % (_('seedless'))
- self.setWindowTitle( title )
- self.update_wallet()
- # set initial message
- self.console.showMessage(self.network.banner)
- # 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()
+ 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):
# account selector
accounts = self.wallet.get_account_names()
self.account_selector.clear()
else:
self.account_selector.hide()
- self.new_account.setEnabled(self.wallet.seed_version>4)
+ def load_wallet(self, wallet):
+ import electrum
+ self.wallet = wallet
+ self.accounts_expanded = self.wallet.storage.get('accounts_expanded',{})
+ self.current_account = self.wallet.storage.get("current_account", None)
+
+ title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.wallet.storage.path
+ if self.wallet.is_watching_only(): title += ' [%s]' % (_('watching only'))
+ self.setWindowTitle( title )
+ self.update_wallet()
+ # 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)
self.update_lock_icon()
self.update_buttons_on_seed()
self.update_console()
- run_hook('load_wallet')
-
-
- def select_wallet_file(self):
- wallet_folder = self.wallet.storage.path
- re.sub("(\/\w*.dat)$", "", wallet_folder)
- file_name = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) )
- return file_name
+ run_hook('load_wallet', wallet)
def open_wallet(self):
-
- filename = self.select_wallet_file()
+ wallet_folder = self.wallet.storage.path
+ filename = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) )
if not filename:
return
import shutil
path = self.wallet.storage.path
wallet_folder = os.path.dirname(path)
- new_filename, ok = QInputDialog.getText(self, _('Filename'), _('Current directory') + ': ' + wallet_folder + '\n' + _('Enter a filename for the copy of your wallet') + ':')
- new_filename = unicode(new_filename)
- if not ok or not new_filename:
+ filename = unicode( QFileDialog.getSaveFileName(self, _('Enter a filename for the copy of your wallet'), wallet_folder) )
+ if not filename:
return
- new_path = os.path.join(wallet_folder, new_filename)
+ new_path = os.path.join(wallet_folder, filename)
if new_path != path:
try:
shutil.copy2(path, new_path)
import installwizard
wallet_folder = os.path.dirname(self.wallet.storage.path)
- filename, ok = QInputDialog.getText(self, _('Filename'), _('Current directory') + ': ' + wallet_folder + '\n'+_('Enter a new file name') + ':')
- filename = unicode(filename)
- if not ok or not filename:
+ filename = unicode( QFileDialog.getSaveFileName(self, _('Enter a new file name'), wallet_folder) )
+ if not filename:
return
filename = os.path.join(wallet_folder, filename)
storage = WalletStorage({'wallet_path': filename})
- assert not storage.file_exists
+ if storage.file_exists:
+ QMessageBox.critical(None, "Error", _("File exists"))
+ return
wizard = installwizard.InstallWizard(self.config, self.network, storage)
wallet = wizard.run()
wallet_menu.addSeparator()
- #if self.wallet.seed:
+ 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)
wallet_menu.addSeparator()
- csv_transaction_menu = wallet_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)
+ 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)
- raw_transaction_menu = wallet_menu.addMenu(_("&Load transaction"))
+ 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)
- raw_transaction_file = raw_transaction_menu.addAction(_("&From file"))
- raw_transaction_file.triggered.connect(self.do_process_from_file)
+ ex_history = wallet_menu.addAction(_("&Export History"))
+ ex_history.triggered.connect(self.do_export_history)
- raw_transaction_text = raw_transaction_menu.addAction(_("&From text"))
- raw_transaction_text.triggered.connect(self.do_process_from_text)
tools_menu = menubar.addMenu(_("&Tools"))
preferences_menu = tools_menu.addAction(preferences_name)
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)
- wallet_menu.addSeparator()
+ tools_menu.addSeparator()
- labels_menu = tools_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)
+ csv_transaction_menu = tools_menu.addMenu(_("&Create transaction"))
- keys_menu = tools_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)
+ csv_transaction_file = csv_transaction_menu.addAction(_("&From CSV file"))
+ csv_transaction_file.triggered.connect(self.do_process_from_csv_file)
- ex_history = tools_menu.addAction(_("&Export History"))
- ex_history.triggered.connect(self.do_export_history)
+ csv_transaction_text = csv_transaction_menu.addAction(_("&From CSV text"))
+ csv_transaction_text.triggered.connect(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)
help_menu = menubar.addMenu(_("&Help"))
def notify_transactions(self):
+ if not self.network or not self.network.is_connected():
+ return
+
print_error("Notifying GUI")
if len(self.network.interface.pending_transactions_for_notifications) > 0:
# Combine the transactions if there are more then three
- def set_label(self, name, text = None):
- changed = False
- old_text = self.wallet.labels.get(name)
- if text:
- if old_text != text:
- self.wallet.labels[name] = text
- self.wallet.storage.put('labels', self.wallet.labels)
- changed = True
- else:
- if old_text:
- self.wallet.labels.pop(name)
- changed = True
- run_hook('set_label', name, text, changed)
- return changed
-
-
# custom wrappers for getOpenFileName and getSaveFileName, that remember the path selected by the user
def getOpenFileName(self, title, filter = ""):
directory = self.config.get('io_dir', os.path.expanduser('~'))
assert self.decimal_point in [5,8]
return "BTC" if self.decimal_point == 8 else "mBTC"
- def set_status_text(self, text):
- self.balance_label.setText(text)
- run_hook('set_status_text', text)
-
def update_status(self):
- if self.network.interface and self.network.interface.is_connected:
+ if self.network is None:
+ text = _("Offline")
+ icon = QIcon(":icons/status_disconnected.png")
+
+ elif self.network.is_connected():
if not self.wallet.up_to_date:
text = _("Synchronizing...")
icon = QIcon(":icons/status_waiting.png")
+ elif self.network.server_lag > 1:
+ text = _("Server is lagging (%d blocks)"%self.network.server_lag)
+ icon = QIcon(":icons/status_lagging.png")
else:
c, u = self.wallet.get_account_balance(self.current_account)
text = _( "Balance" ) + ": %s "%( self.format_amount(c) ) + self.base_unit()
if u: text += " [%s unconfirmed]"%( self.format_amount(u,True).strip() )
+
+ r = {}
+ run_hook('set_quote_text', c+u, r)
+ quote = r.get(0)
+ if quote:
+ text += " (%s)"%quote
+
self.tray.setToolTip(text)
icon = QIcon(":icons/status_connected.png")
else:
text = _("Not connected")
icon = QIcon(":icons/status_disconnected.png")
- self.set_status_text(text)
+ self.balance_label.setText(text)
self.status_button.setIcon( icon )
+
def update_wallet(self):
self.update_status()
- if self.wallet.up_to_date or not self.network.interface.is_connected:
+ if self.wallet.up_to_date or not self.network or not self.network.is_connected():
self.update_history_tab()
self.update_receive_tab()
self.update_contacts_tab()
self.update_completions()
-
def create_history_tab(self):
self.history_list = l = MyTreeWidget(self)
tx_hash = str(item.data(0, Qt.UserRole).toString())
tx = self.wallet.transactions.get(tx_hash)
text = unicode( item.text(2) )
- self.set_label(tx_hash, text)
+ self.wallet.set_label(tx_hash, text)
if text:
item.setForeground(2, QBrush(QColor('black')))
else:
if not is_editable:
return
- changed = self.set_label(addr, text)
+ changed = self.wallet.set_label(addr, text)
if changed:
self.update_history_tab()
self.update_completions()
self.history_list.clear()
for item in self.wallet.get_tx_history(self.current_account):
tx_hash, conf, is_mine, value, fee, balance, timestamp = item
+ time_str = _("unknown")
if conf > 0:
try:
time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
- except:
- time_str = _("unknown")
+ except Exception:
+ time_str = _("error")
if conf == -1:
time_str = 'unverified'
if self.amount_e.is_shortcut:
self.amount_e.is_shortcut = False
c, u = self.wallet.get_account_balance(self.current_account)
- inputs, total, fee = self.wallet.choose_tx_inputs( c + u, 0, self.current_account)
+ inputs, total, fee = self.wallet.choose_tx_inputs_from_account( c + u, 0, self.current_account)
fee = self.wallet.estimated_fee(inputs)
- amount = c + u - fee
+ amount = total - fee
self.amount_e.setText( self.format_amount(amount) )
self.fee_e.setText( self.format_amount( fee ) )
return
if not is_fee: fee = None
if amount is None:
return
- inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee, self.current_account )
+ inputs, total, fee = self.wallet.choose_tx_inputs_from_account( amount, fee, self.current_account )
if not is_fee:
self.fee_e.setText( self.format_amount( fee ) )
if inputs:
try:
amount = self.read_amount(unicode( self.amount_e.text()))
- except:
+ except Exception:
QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
return
try:
fee = self.read_amount(unicode( self.fee_e.text()))
- except:
+ except Exception:
QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
return
try:
tx = self.wallet.mktx_from_account( [(to_address, amount)], password, fee, self.current_account)
- except BaseException, e:
+ except Exception as e:
traceback.print_exc(file=sys.stdout)
self.show_message(str(e))
return
QMessageBox.warning(self, _('Error'), _("This transaction requires a higher fee, or it will not be propagated by the network."), _('OK'))
return
- run_hook('send_tx', tx)
-
if label:
- self.set_label(tx.hash(), label)
+ self.wallet.set_label(tx.hash(), label)
if tx.is_complete:
h = self.wallet.send_tx(tx)
else:
QMessageBox.warning(self, _('Error'), msg, _('OK'))
else:
- filename = label + '.txn' if label else 'unsigned_%s.txn' % (time.mktime(time.gmtime()))
- try:
- fileName = self.getSaveFileName(_("Select a transaction filename"), filename, "*.txn")
- with open(fileName,'w') as f:
- f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
- QMessageBox.information(self, _('Unsigned transaction created'), _("Unsigned transaction was saved to file:") + " " +fileName, _('OK'))
- except:
- QMessageBox.warning(self, _('Error'), _('Could not write transaction to file'), _('OK'))
+
+ self.show_transaction(tx)
# add recipient to addressbook
if to_address not in self.wallet.addressbook and not self.wallet.is_mine(to_address):
def set_url(self, url):
address, amount, label, message, signature, identity, url = util.parse_url(url)
- if self.base_unit() == 'mBTC': amount = str( 1000* Decimal(amount))
+
+ if amount and self.base_unit() == 'mBTC': amount = str( 1000* Decimal(amount))
+
+ if self.mini:
+ self.mini.set_payment_fields(address, amount)
if label and self.wallet.labels.get(address) != label:
if self.question('Give label "%s" to address %s ?'%(label,address)):
if address not in self.wallet.addressbook and not self.wallet.is_mine(address):
self.wallet.addressbook.append(address)
- self.set_label(address, label)
+ self.wallet.set_label(address, label)
run_hook('set_url', url, self.show_message, self.question)
self.payto_e.setText(m_addr)
self.message_e.setText(message)
- self.amount_e.setText(amount)
+ if amount:
+ self.amount_e.setText(amount)
+
if identity:
self.set_frozen(self.payto_e,True)
self.set_frozen(self.amount_e,True)
return w
- def receive_tab_set_mode(self, i):
- self.save_column_widths()
- self.expert_mode = (i == 1)
- self.config.set_key('classic_expert_mode', self.expert_mode, True)
- self.update_receive_tab()
def save_column_widths(self):
- if not self.expert_mode:
- widths = [ self.receive_list.columnWidth(0) ]
- else:
- widths = []
- for i in range(self.receive_list.columnCount() -1):
- widths.append(self.receive_list.columnWidth(i))
- self.column_widths["receive"][self.expert_mode] = widths
+ self.column_widths["receive"] = []
+ for i in range(self.receive_list.columnCount() -1):
+ self.column_widths["receive"].append(self.receive_list.columnWidth(i))
self.column_widths["history"] = []
for i in range(self.history_list.columnCount() - 1):
for i in range(self.contacts_list.columnCount() - 1):
self.column_widths["contacts"].append(self.contacts_list.columnWidth(i))
- self.config.set_key("column_widths", self.column_widths, True)
+ self.config.set_key("column_widths_2", self.column_widths, True)
def create_contacts_tab(self):
self.update_receive_tab()
self.update_history_tab()
+ def edit_account_label(self, k):
+ text, ok = QInputDialog.getText(self, _('Rename account'), _('Name') + ':', text = self.wallet.labels.get(k,''))
+ if ok:
+ label = unicode(text)
+ self.wallet.set_label(k,label)
+ self.update_receive_tab()
+
+ def account_set_expanded(self, item, k, b):
+ item.setExpanded(b)
+ self.accounts_expanded[k] = b
+
+ def create_account_menu(self, position, k, item):
+ menu = QMenu()
+ if item.isExpanded():
+ menu.addAction(_("Minimize"), lambda: self.account_set_expanded(item, k, 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.account_is_pending(k):
+ menu.addAction(_("Delete"), lambda: self.delete_pending_account(k))
+ menu.exec_(self.receive_list.viewport().mapToGlobal(position))
+
+ def delete_pending_account(self, k):
+ self.wallet.delete_pending_account(k)
+ self.update_receive_tab()
def create_receive_menu(self, position):
# fixme: this function apparently has a side effect.
item = self.receive_list.itemAt(position)
if not item: return
+
addr = unicode(item.text(0))
if not is_valid(addr):
- item.setExpanded(not item.isExpanded())
+ k = str(item.data(0,32).toString())
+ if k:
+ self.create_account_menu(position, k, item)
+ else:
+ item.setExpanded(not item.isExpanded())
return
+
menu = QMenu()
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))
- menu.addAction(_("Private key"), lambda: self.show_private_key(addr))
- menu.addAction(_("Sign message"), lambda: self.sign_message(addr))
+ if self.wallet.seed:
+ menu.addAction(_("Private key"), lambda: self.show_private_key(addr))
+ 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.expert_mode:
- t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
- menu.addAction(t, lambda: self.toggle_freeze(addr))
- t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
- menu.addAction(t, lambda: self.toggle_priority(addr))
+ t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
+ menu.addAction(t, lambda: self.toggle_freeze(addr))
+ t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
+ menu.addAction(t, lambda: self.toggle_priority(addr))
run_hook('receive_menu', menu)
menu.exec_(self.receive_list.viewport().mapToGlobal(position))
def delete_contact(self, x):
if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
self.wallet.delete_contact(x)
- self.set_label(x, None)
+ self.wallet.set_label(x, None)
self.update_history_tab()
self.update_contacts_tab()
self.update_completions()
item.setData(0,32, True) # is editable
run_hook('update_receive_item', address, item)
-
+
+ if not self.wallet.is_mine(address): return
+
c, u = self.wallet.get_addr_balance(address)
balance = self.format_amount(c + u)
item.setData(2,0,balance)
- if self.expert_mode:
- if address in self.wallet.frozen_addresses:
- item.setBackgroundColor(0, QColor('lightblue'))
- elif address in self.wallet.prioritized_addresses:
- item.setBackgroundColor(0, QColor('lightgreen'))
+ if address in self.wallet.frozen_addresses:
+ item.setBackgroundColor(0, QColor('lightblue'))
+ elif address in self.wallet.prioritized_addresses:
+ item.setBackgroundColor(0, QColor('lightgreen'))
def update_receive_tab(self):
l = self.receive_list
l.clear()
- l.setColumnHidden(2, not self.expert_mode)
- l.setColumnHidden(3, not self.expert_mode)
- for i,width in enumerate(self.column_widths['receive'][self.expert_mode]):
+ l.setColumnHidden(2, False)
+ l.setColumnHidden(3, False)
+ for i,width in enumerate(self.column_widths['receive']):
l.setColumnWidth(i, width)
if self.current_account is None:
c,u = self.wallet.get_account_balance(k)
account_item = QTreeWidgetItem( [ name, '', self.format_amount(c+u), ''] )
l.addTopLevelItem(account_item)
- account_item.setExpanded(True)
+ 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)
- for is_change in ([0,1] if self.expert_mode else [0]):
- if self.expert_mode:
- name = _("Receiving") if not is_change else _("Change")
- seq_item = QTreeWidgetItem( [ name, '', '', '', ''] )
- account_item.addChild(seq_item)
- if not is_change: seq_item.setExpanded(True)
- else:
- seq_item = account_item
+ for is_change in ([0,1]):
+ name = _("Receiving") if not is_change else _("Change")
+ seq_item = QTreeWidgetItem( [ name, '', '', '', ''] )
+ account_item.addChild(seq_item)
+ if not is_change: seq_item.setExpanded(True)
+
is_red = False
gap = 0
seq_item.addChild(item)
+ for k, addr in self.wallet.get_pending_accounts():
+ name = self.wallet.labels.get(k,'')
+ account_item = QTreeWidgetItem( [ name + " [ "+_('pending account')+" ]", '', '', ''] )
+ self.update_receive_item(item)
+ l.addTopLevelItem(account_item)
+ account_item.setExpanded(True)
+ account_item.setData(0, 32, k)
+ item = QTreeWidgetItem( [ addr, '', '', '', ''] )
+ account_item.addChild(item)
+ self.update_receive_item(item)
+
+
if self.wallet.imported_keys and (self.current_account is None or self.current_account == -1):
c,u = self.wallet.get_imported_balance()
account_item = QTreeWidgetItem( [ _('Imported'), '', self.format_amount(c+u), ''] )
def mkfunc(f, method):
return lambda *args: apply( f, (method, args, self.password_dialog ))
for m in dir(c):
- if m[0]=='_' or m=='wallet' or m == 'interface': continue
+ if m[0]=='_' or m in ['network','wallet']: continue
methods[m] = mkfunc(c._run, m)
console.updateNamespace(methods)
def update_buttons_on_seed(self):
- if self.wallet.seed:
+ if not self.wallet.is_watching_only():
self.seed_button.show()
self.password_button.show()
self.send_button.setText(_("Send"))
def new_contact_dialog(self):
- text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
- address = unicode(text)
- if ok:
- if is_valid(address):
- self.wallet.add_contact(address)
- self.update_contacts_tab()
- self.update_history_tab()
- self.update_completions()
- else:
- QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
+
+ d = QDialog(self)
+ vbox = QVBoxLayout(d)
+ vbox.addWidget(QLabel(_('New Contact')+':'))
+
+ grid = QGridLayout()
+ line1 = QLineEdit()
+ line2 = QLineEdit()
+ grid.addWidget(QLabel(_("Address")), 1, 0)
+ grid.addWidget(line1, 1, 1)
+ grid.addWidget(QLabel(_("Name")), 2, 0)
+ grid.addWidget(line2, 2, 1)
+
+ vbox.addLayout(grid)
+ vbox.addLayout(ok_cancel_buttons(d))
+
+ if not d.exec_():
+ return
+
+ address = str(line1.text())
+ label = unicode(line2.text())
+
+ if not is_valid(address):
+ QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
+ return
+
+ self.wallet.add_contact(address)
+ if label:
+ self.wallet.set_label(address, label)
+
+ self.update_contacts_tab()
+ self.update_history_tab()
+ self.update_completions()
+ self.tabs.setCurrentIndex(3)
def new_account_dialog(self):
dialog.setModal(1)
dialog.setWindowTitle(_("New Account"))
- addr = self.wallet.new_account_address()
vbox = QVBoxLayout()
- msg = _("Electrum considers that an account exists only if it contains bitcoins.") + '\n' \
- + _("To create a new account, please send coins to the first address of that account.") + '\n' \
- + _("Note: you will need to wait for 2 confirmations before the account is created.")
- vbox.addWidget(QLabel(msg))
- vbox.addWidget(QLabel(_('Address')+':'))
- e = QLineEdit(addr)
- e.setReadOnly(True)
+ vbox.addWidget(QLabel(_('Account name')+':'))
+ e = QLineEdit()
vbox.addWidget(e)
+ msg = _("Note: Newly created accounts are 'pending' until they receive bitcoins.") + " " \
+ + _("You will need to wait for 2 confirmations until the correct balance is displayed and more addresses are created for that account.")
+ l = QLabel(msg)
+ l.setWordWrap(True)
+ vbox.addWidget(l)
vbox.addLayout(ok_cancel_buttons(dialog))
dialog.setLayout(vbox)
r = dialog.exec_()
- if r:
- self.payto(addr)
+ if not r: return
+
+ name = str(e.text())
+ if not name: return
+ self.wallet.create_pending_account('1', name)
+ self.update_receive_tab()
+ self.tabs.setCurrentIndex(2)
+
- def show_master_public_key(self):
+ def show_master_public_key_old(self):
dialog = QDialog(self)
dialog.setModal(1)
dialog.setWindowTitle(_("Master Public Key"))
vbox = QVBoxLayout()
vbox.addLayout(main_layout)
- hbox = QHBoxLayout()
- hbox.addStretch(1)
- hbox.addWidget(ok_button)
- vbox.addLayout(hbox)
+ 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
+
+ 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])
+
+ vbox = QVBoxLayout()
+ vbox.addLayout(main_layout)
+ vbox.addLayout(close_button(dialog))
dialog.setLayout(vbox)
dialog.exec_()
@protected
def show_seed_dialog(self, password):
- if not self.wallet.seed:
- QMessageBox.information(parent, _('Message'), _('No seed'), _('OK'))
- return
- try:
- seed = self.wallet.decode_seed(password)
- except:
- QMessageBox.warning(self, _('Error'), _('Incorrect Password'), _('OK'))
+ if self.wallet.is_watching_only():
+ QMessageBox.information(self, _('Message'), _('This is a watching-only wallet'), _('OK'))
return
- from seed_dialog import SeedDialog
- d = SeedDialog(self)
- d.show_seed(seed, self.wallet.imported_keys)
+ 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_()
+
+
hbox = QHBoxLayout()
hbox.addStretch(1)
- def print_qr(self):
- filename = "qrcode.bmp"
+ filename = os.path.join(self.config.path, "qrcode.bmp")
+
+ def print_qr():
bmp.save_qrcode(qrw.qr, filename)
QMessageBox.information(None, _('Message'), _("QR code saved to file") + " " + filename, _('OK'))
+ def copy_to_clipboard():
+ bmp.save_qrcode(qrw.qr, filename)
+ self.app.clipboard().setImage(QImage(filename))
+ QMessageBox.information(None, _('Message'), _("QR code saved to clipboard"), _('OK'))
+
+ b = QPushButton(_("Copy"))
+ hbox.addWidget(b)
+ b.clicked.connect(copy_to_clipboard)
+
b = QPushButton(_("Save"))
hbox.addWidget(b)
b.clicked.connect(print_qr)
if not address: return
try:
pk_list = self.wallet.get_private_key(address, password)
- except BaseException, e:
+ except Exception as e:
self.show_message(str(e))
return
QMessageBox.information(self, _('Private key'), _('Address')+ ': ' + address + '\n\n' + _('Private key') + ': ' + '\n'.join(pk_list), _('OK'))
try:
sig = self.wallet.sign_message(str(address.text()), message, password)
signature.setText(sig)
- except BaseException, e:
+ except Exception as e:
self.show_message(str(e))
def sign_message(self, address):
def do_verify():
message = unicode(verify_message.toPlainText())
message = message.encode('utf-8')
- if self.wallet.verify_message(verify_address.text(), str(verify_signature.toPlainText()), message):
+ if bitcoin.verify_message(verify_address.text(), str(verify_signature.toPlainText()), message):
self.show_message(_("Signature verified"))
else:
self.show_message(_("Error: wrong signature"))
txt.decode('hex')
tx = Transaction(txt)
return tx
- except:
+ except Exception:
pass
try:
tx_dict = json.loads(str(txt))
assert "hex" in tx_dict.keys()
assert "complete" in tx_dict.keys()
+ tx = Transaction(tx_dict["hex"], tx_dict["complete"])
if not tx_dict["complete"]:
assert "input_info" in tx_dict.keys()
- tx = Transaction(tx_dict["hex"])
+ input_info = json.loads(tx_dict['input_info'])
+ tx.add_input_info(input_info)
return tx
- except:
+ except Exception:
pass
QMessageBox.critical(None, _("Unable to parse transaction"), _("Electrum was unable to parse your transaction"))
try:
tx = self.wallet.make_unsigned_transaction(outputs, None, None)
- except BaseException, e:
+ except Exception as e:
self.show_message(str(e))
return
return
def do_process_from_csv_text(self):
- text = text_dialog(self, _('Input CSV'), _("CSV:"), _("Load CSV"))
+ text = text_dialog(self, _('Input CSV'), _("Please enter a list of outputs.") + '\n' \
+ + _("Format: address, amount. One output per line"), _("Load CSV"))
if not text:
return
f = StringIO.StringIO(text)
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:
+ except Exception as e:
self.show_message(str(e))
return
for key in text:
try:
addr = self.wallet.import_key(key, password)
- except BaseException as e:
+ except Exception as e:
badkeys.append(key)
continue
if not addr:
d.setWindowTitle(_('Electrum Settings'))
d.setModal(1)
vbox = QVBoxLayout()
+ grid = QGridLayout()
+ grid.setColumnStretch(0,1)
- 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_label = QLabel(_('Display zeros') + ':')
+ grid.addWidget(nz_label, 0, 0)
nz_e = AmountEdit(None,True)
nz_e.setText("%d"% self.num_zeros)
- grid_ui.addWidget(nz_e, 0, 1)
+ grid.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)
+ grid.addWidget(HelpButton(msg), 0, 2)
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)
+ grid.addWidget(lang_label, 1, 0)
lang_combo = QComboBox()
from electrum.i18n import languages
lang_combo.addItems(languages.values())
try:
index = languages.keys().index(self.config.get("language",''))
- except:
+ except Exception:
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)
+ grid.addWidget(lang_combo, 1, 1)
+ grid.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)
- expert_cb = QCheckBox(_('Expert mode'))
- expert_cb.setChecked(self.expert_mode)
- grid_ui.addWidget(expert_cb, 3, 0)
- hh = _('In expert mode, your client will:') + '\n' \
- + _(' - Show change addresses in the Receive tab') + '\n' \
- + _(' - Display the balance of each address') + '\n' \
- + _(' - Add freeze/prioritize actions to addresses.')
- 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_label = QLabel(_('Transaction fee') + ':')
+ grid.addWidget(fee_label, 2, 0)
fee_e = AmountEdit(self.base_unit)
fee_e.setText(self.format_amount(self.wallet.fee).strip())
- grid_wallet.addWidget(fee_e, 0, 2)
+ grid.addWidget(fee_e, 2, 1)
msg = _('Fee per kilobyte of transaction.') + ' ' \
+ _('Recommended value') + ': ' + self.format_amount(50000)
- grid_wallet.addWidget(HelpButton(msg), 0, 3)
+ grid.addWidget(HelpButton(msg), 2, 2)
if not self.config.is_modifiable('fee_per_kb'):
for w in [fee_e, fee_label]: w.setEnabled(False)
- usechange_cb = QCheckBox(_('Use change addresses'))
- usechange_cb.setChecked(self.wallet.use_change)
- grid_wallet.addWidget(usechange_cb, 1, 0)
- 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_cb.setEnabled(False)
-
units = ['BTC', 'mBTC']
- unit_label = QLabel(_('Base unit'))
- grid_wallet.addWidget(unit_label, 3, 0)
+ unit_label = QLabel(_('Base unit') + ':')
+ grid.addWidget(unit_label, 3, 0)
unit_combo = QComboBox()
unit_combo.addItems(units)
unit_combo.setCurrentIndex(units.index(self.base_unit()))
- grid_wallet.addWidget(unit_combo, 3, 2)
- grid_wallet.addWidget(HelpButton(_('Base unit of your wallet.')\
+ grid.addWidget(unit_combo, 3, 1)
+ grid.addWidget(HelpButton(_('Base unit of your wallet.')\
+ '\n1BTC=1000mBTC.\n' \
- + _(' This settings affects the fields in the Send tab')+' '), 3, 3)
- grid_wallet.setRowStretch(4,1)
+ + _(' This settings affects the fields in the Send tab')+' '), 3, 2)
+ usechange_cb = QCheckBox(_('Use change addresses'))
+ usechange_cb.setChecked(self.wallet.use_change)
+ grid.addWidget(usechange_cb, 4, 0)
+ 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)
- run_hook('create_settings_tab', tabs)
+ grid.setRowStretch(5,1)
+ vbox.addLayout(grid)
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
fee = unicode(fee_e.text())
try:
fee = self.read_amount(fee)
- except:
+ except Exception:
QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
return
try:
nz = int( nz )
if nz>8: nz=8
- except:
+ except Exception:
QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
return
usechange_result = usechange_cb.isChecked()
if self.wallet.use_change != usechange_result:
self.wallet.use_change = usechange_result
- self.config.set_key('use_change', self.wallet.use_change, True)
+ self.wallet.storage.put('use_change', self.wallet.use_change)
unit_result = units[unit_combo.currentIndex()]
if self.base_unit() != unit_result:
self.config.set_key("language", lang_request, True)
need_restart = True
-
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(expert_cb.isChecked())
def run_network_dialog(self):
+ if not self.network:
+ return
NetworkDialog(self.wallet.network, self.config, self).do_exec()
def closeEvent(self, event):
self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
self.save_column_widths()
self.config.set_key("console-history", self.console.history[-50:], True)
+ self.wallet.storage.put('accounts_expanded', self.accounts_expanded)
event.accept()
grid.setColumnStretch(0,1)
w.setLayout(grid)
- def mk_toggle(cb, p):
- return lambda: cb.setChecked(p.toggle())
+ def do_toggle(cb, p, w):
+ r = p.toggle()
+ cb.setChecked(r)
+ if w: w.setEnabled(r)
+
+ def mk_toggle(cb, p, w):
+ return lambda: do_toggle(cb,p,w)
+
for i, p in enumerate(plugins):
try:
cb = QCheckBox(p.fullname())
cb.setDisabled(not p.is_available())
cb.setChecked(p.is_enabled())
- cb.clicked.connect(mk_toggle(cb,p))
grid.addWidget(cb, i, 0)
if p.requires_settings():
- grid.addWidget(EnterButton(_('Settings'), p.settings_dialog), i, 1)
+ w = p.settings_widget(self)
+ w.setEnabled( p.is_enabled() )
+ grid.addWidget(w, i, 1)
+ else:
+ w = None
+ cb.clicked.connect(mk_toggle(cb,p,w))
grid.addWidget(HelpButton(p.description()), i, 2)
- except:
+ except Exception:
print_msg(_("Error: cannot display plugin"), p)
traceback.print_exc(file=sys.stdout)
grid.setRowStretch(i+1,1)
vbox.addLayout(close_button(d))
d.exec_()
+
+
+ def show_account_details(self, 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))
+
+ label = QLabel('Derivation: ' + k)
+ vbox.addWidget(label)
+
+ #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)
+
+ vbox.addLayout(close_button(d))
+ d.exec_()