diff --git a/electrum b/electrum index ce81f032..16f94265 100755 --- a/electrum +++ b/electrum @@ -34,8 +34,10 @@ except ImportError: sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'") +is_android = 'ANDROID_DATA' in os.environ + # load local module as electrum -if os.path.exists("lib"): +if os.path.exists("lib") or is_android: import imp fp, pathname, description = imp.find_module('lib') imp.load_module('electrum', fp, pathname, description) @@ -89,8 +91,8 @@ if __name__ == '__main__': set_verbosity(options.verbose) # config is an object passed to the various constructors (wallet, interface, gui) - if 'ANDROID_DATA' in os.environ: - config_options = {'wallet_path':"/sdcard/electrum.dat", 'portable':True, 'verbose':True, 'gui':'android'} + if is_android: + config_options = {'wallet_path':"/sdcard/electrum.dat", 'portable':True, 'verbose':True, 'gui':'android', 'auto_cycle':True} else: config_options = eval(str(options)) for k, v in config_options.items(): diff --git a/gui/gui_classic.py b/gui/gui_classic.py index fc15d27f..dc7bc966 100644 --- a/gui/gui_classic.py +++ b/gui/gui_classic.py @@ -325,6 +325,9 @@ class ElectrumWindow(QMainWindow): # set initial message self.console.showMessage(self.wallet.banner) + # plugins that need to change the GUI do it here + self.run_hook('init') + # plugins def init_plugins(self): @@ -357,11 +360,27 @@ class ElectrumWindow(QMainWindow): if callback in h: h.remove(callback) self.plugin_hooks[name] = h - def run_hook(self, name, args): + def run_hook(self, name, args = ()): + args = (self,) + args for cb in self.plugin_hooks.get(name,[]): apply(cb, args) + 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 + changed = True + else: + if old_text: + self.wallet.labels.pop(name) + changed = True + self.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 = None): directory = self.config.get('io_dir', os.path.expanduser('~')) @@ -382,14 +401,14 @@ class ElectrumWindow(QMainWindow): def close(self): QMainWindow.close(self) - self.run_hook('close_main_window', (self,)) + self.run_hook('close_main_window') def connect_slots(self, sender): self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions) self.previous_payto_e='' def timer_actions(self): - self.run_hook('timer_actions', (self,)) + self.run_hook('timer_actions') if self.payto_e.hasFocus(): return @@ -534,13 +553,11 @@ class ElectrumWindow(QMainWindow): self.is_edit=True tx_hash = str(item.data(0, Qt.UserRole).toString()) tx = self.wallet.transactions.get(tx_hash) - s = self.wallet.labels.get(tx_hash) text = unicode( item.text(2) ) + self.set_label(tx_hash, text) if text: - self.wallet.labels[tx_hash] = text item.setForeground(2, QBrush(QColor('black'))) else: - if s: self.wallet.labels.pop(tx_hash) text = self.wallet.get_default_label(tx_hash) item.setText(2, text) item.setForeground(2, QBrush(QColor('gray'))) @@ -574,33 +591,23 @@ class ElectrumWindow(QMainWindow): text = unicode( item.text(column_label) ) changed = False - if text: - if text not in self.wallet.aliases.keys(): - old_addr = self.wallet.labels.get(text) - if old_addr != addr: - self.wallet.labels[addr] = text - changed = True - else: - print_error("Error: This is one of your aliases") - label = self.wallet.labels.get(addr,'') - item.setText(column_label, QString(label)) + if text in self.wallet.aliases.keys(): + print_error("Error: This is one of your aliases") + label = self.wallet.labels.get(addr,'') + item.setText(column_label, QString(label)) else: - s = self.wallet.labels.get(addr) - if s: - self.wallet.labels.pop(addr) - changed = True - - if changed: - self.update_history_tab() - self.update_completions() + changed = self.set_label(addr, text) + if changed: + self.update_history_tab() + self.update_completions() self.current_item_changed(item) - self.run_hook('item_changed',(self, item, column)) + self.run_hook('item_changed', (item, column)) def current_item_changed(self, a): - self.run_hook('current_item_changed',(self, a)) + self.run_hook('current_item_changed', (a,)) @@ -746,7 +753,7 @@ class ElectrumWindow(QMainWindow): self.amount_e.textChanged.connect(lambda: entry_changed(False) ) self.fee_e.textChanged.connect(lambda: entry_changed(True) ) - self.run_hook('create_send_tab',(self,grid)) + self.run_hook('create_send_tab', (grid,)) return w2 @@ -806,10 +813,10 @@ class ElectrumWindow(QMainWindow): self.show_message(str(e)) return - self.run_hook('send_tx', (self.wallet, self, tx)) + self.run_hook('send_tx', (tx,)) if label: - self.wallet.labels[tx.hash()] = label + self.set_label(tx.hash(), label) if tx.is_complete: h = self.wallet.send_tx(tx) @@ -1002,7 +1009,7 @@ class ElectrumWindow(QMainWindow): t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize") menu.addAction(t, lambda: self.toggle_priority(addr)) - self.run_hook('receive_menu', (self, menu,)) + self.run_hook('receive_menu', (menu,)) menu.exec_(self.receive_list.viewport().mapToGlobal(position)) @@ -1023,8 +1030,7 @@ class ElectrumWindow(QMainWindow): if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")): if not is_alias and x in self.wallet.addressbook: self.wallet.addressbook.remove(x) - if x in self.wallet.labels.keys(): - self.wallet.labels.pop(x) + self.set_label(x, None) elif is_alias and x in self.wallet.aliases: self.wallet.aliases.pop(x) self.update_history_tab() @@ -1060,7 +1066,7 @@ class ElectrumWindow(QMainWindow): label = self.wallet.labels.get(address,'') item.setData(1,0,label) - self.run_hook('update_receive_item', (self, address, item)) + self.run_hook('update_receive_item', (address, item)) c, u = self.wallet.get_addr_balance(address) balance = format_satoshis( c + u, False, self.wallet.num_zeros ) @@ -1679,7 +1685,7 @@ class ElectrumWindow(QMainWindow): if not tx_dict["complete"]: assert "input_info" in tx_dict.keys() except: - QMessageBox.critical(None, "Unable to parse transaction", _("Electrum was unable to parse your transaction:")) + QMessageBox.critical(None, "Unable to parse transaction", _("Electrum was unable to parse your transaction")) return None return tx_dict @@ -1706,7 +1712,7 @@ class ElectrumWindow(QMainWindow): if fileName: with open(fileName, "w+") as f: f.write(json.dumps(tx.as_dict(),indent=4) + '\n') - self.show_message(_("Transaction saved succesfully")) + self.show_message(_("Transaction saved successfully")) if dialog: dialog.done(0) except BaseException, e: @@ -1716,7 +1722,7 @@ class ElectrumWindow(QMainWindow): def send_raw_transaction(self, raw_tx, dialog = ""): result, result_message = self.wallet.sendtx( raw_tx ) if result: - self.show_message("Transaction succesfully sent: %s" % (result_message)) + self.show_message("Transaction successfully sent: %s" % (result_message)) if dialog: dialog.done(0) else: @@ -1759,7 +1765,7 @@ class ElectrumWindow(QMainWindow): l = QGridLayout() dialog.setLayout(l) - l.addWidget(QLabel(_("Transaction status: ")), 3,0) + l.addWidget(QLabel(_("Transaction status:")), 3,0) l.addWidget(QLabel(_("Actions")), 4,0) if tx_dict["complete"] == False: diff --git a/gui/i18n.py b/gui/i18n.py index 9436219c..d2a8ddef 100644 --- a/gui/i18n.py +++ b/gui/i18n.py @@ -44,10 +44,12 @@ languages = { 'es':_('Spanish'), 'fr':_('French'), 'it':_('Italian'), + 'ja':_('Japanese'), 'lv':_('Latvian'), 'nl':_('Dutch'), 'ru':_('Russian'), 'sl':_('Slovenian'), + 'ta':_('Tamil'), 'vi':_('Vietnamese'), 'zh':_('Chinese') } diff --git a/gui/qt_console.py b/gui/qt_console.py index ff579f09..49d4148a 100644 --- a/gui/qt_console.py +++ b/gui/qt_console.py @@ -1,12 +1,19 @@ # source: http://stackoverflow.com/questions/2758159/how-to-embed-a-python-interpreter-in-a-pyqt-widget import sys, os, re -import traceback +import traceback, platform from PyQt4 import QtCore from PyQt4 import QtGui from electrum import util +if platform.system() == 'Windows': + MONOSPACE_FONT = 'Lucida Console' +elif platform.system() == 'Darwin': + MONOSPACE_FONT = 'Monaco' +else: + MONOSPACE_FONT = 'monospace' + class Console(QtGui.QPlainTextEdit): def __init__(self, prompt='>> ', startup_message='', parent=None): @@ -20,7 +27,7 @@ class Console(QtGui.QPlainTextEdit): self.setGeometry(50, 75, 600, 400) self.setWordWrapMode(QtGui.QTextOption.WrapAnywhere) self.setUndoRedoEnabled(False) - self.document().setDefaultFont(QtGui.QFont("monospace", 10, QtGui.QFont.Normal)) + self.document().setDefaultFont(QtGui.QFont(MONOSPACE_FONT, 10, QtGui.QFont.Normal)) self.showMessage(startup_message) self.updateNamespace({'run':self.run_script}) diff --git a/lib/bitcoin.py b/lib/bitcoin.py index a3403fef..1bfee8c9 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -825,7 +825,7 @@ class Transaction: for i in self.inputs: e = { 'txid':i['tx_hash'], 'vout':i['index'], 'scriptPubKey':i.get('raw_output_script'), - 'electrumKeyID':i.get('electrumKeyID'), + 'KeyID':i.get('KeyID'), 'redeemScript':i.get('redeemScript'), 'signatures':i.get('signatures'), 'pubkeys':i.get('pubkeys'), diff --git a/lib/version.py b/lib/version.py index 02b04233..1ed992c9 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,4 +1,4 @@ ELECTRUM_VERSION = "1.7" # version of the client package PROTOCOL_VERSION = '0.6' # protocol version requested SEED_VERSION = 4 # bump this every time the seed generation is modified -TRANSLATION_ID = 3958 # version of the wiki page +TRANSLATION_ID = 3992 # version of the wiki page diff --git a/lib/wallet.py b/lib/wallet.py index feff4fc1..2fbae844 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -256,7 +256,7 @@ class Wallet: if item.get('txid') == txin['tx_hash'] and item.get('vout') == txin['index']: txin['raw_output_script'] = item['scriptPubKey'] txin['redeemScript'] = item.get('redeemScript') - txin['electrumKeyID'] = item.get('electrumKeyID') + txin['KeyID'] = item.get('KeyID') break else: for item in unspent_coins: @@ -268,8 +268,9 @@ class Wallet: raise # find the address: - if txin.get('electrumKeyID'): - account, sequence = txin.get('electrumKeyID') + if txin.get('KeyID'): + account, name, sequence = txin.get('KeyID') + if name != 'Electrum': continue sec = self.sequences[account].get_private_key(sequence, seed) addr = self.sequences[account].get_address(sequence) txin['address'] = addr @@ -771,7 +772,7 @@ class Wallet: pk_addresses.append(address) continue account, sequence = self.get_address_index(address) - txin['electrumKeyID'] = (account, sequence) # used by the server to find the key + txin['KeyID'] = (account, 'Electrum', sequence) # used by the server to find the key pk_addr, redeemScript = self.sequences[account].get_input_info(sequence) if redeemScript: txin['redeemScript'] = redeemScript pk_addresses.append(pk_addr) @@ -1197,9 +1198,6 @@ class WalletSynchronizer(threading.Thread): while not self.interface.is_connected: time.sleep(1) - # request banner, because 'connected' event happens before this thread is started - self.interface.send([('server.banner',[])],'synchronizer') - # subscriptions self.subscribe_to_addresses(self.wallet.addresses(True)) diff --git a/make_packages b/make_packages index f079ab4a..936c48a9 100755 --- a/make_packages +++ b/make_packages @@ -20,10 +20,13 @@ if __name__ == '__main__': # android os.system('rm -rf dist/e4a-%s'%version) os.mkdir('dist/e4a-%s'%version) - shutil.copyfile("electrum",'dist/e4a-%s/electrum.py'%version) + shutil.copyfile("electrum",'dist/e4a-%s/e4a.py'%version) shutil.copytree("ecdsa",'dist/e4a-%s/ecdsa'%version) shutil.copytree("aes",'dist/e4a-%s/aes'%version) - shutil.copytree("lib",'dist/e4a-%s/electrum'%version) + shutil.copytree("lib",'dist/e4a-%s/lib'%version) + os.mkdir('dist/e4a-%s/gui'%version) + shutil.copy("gui/gui_android.py",'dist/e4a-%s/gui'%version) + shutil.copy("gui/__init__.py",'dist/e4a-%s/gui'%version) os.chdir("dist") # create the zip file diff --git a/plugins/pointofsale.py b/plugins/pointofsale.py index 87d15147..29dedeb9 100644 --- a/plugins/pointofsale.py +++ b/plugins/pointofsale.py @@ -103,6 +103,7 @@ def init(gui): gui.requested_amounts = config.get('requested_amounts',{}) gui.merchant_name = config.get('merchant_name', 'Invoice') gui.qr_window = None + do_enable(gui, is_enabled()) def is_enabled(): return config.get('pointofsale') is True @@ -110,35 +111,47 @@ def is_enabled(): def is_available(): return True -def toggle(gui): - if not is_enabled(): +def toggle(gui): + enabled = not is_enabled() + config.set_key('pointofsale', enabled, True) + do_enable(gui, enabled) + update_gui(gui) + return enabled + + +def do_enable(gui, enabled): + if enabled: gui.expert_mode = True - gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Request')]) gui.set_hook('item_changed', item_changed) gui.set_hook('current_item_changed', recv_changed) gui.set_hook('receive_menu', receive_menu) gui.set_hook('update_receive_item', update_receive_item) gui.set_hook('timer_actions', timer_actions) gui.set_hook('close_main_window', close_main_window) - enabled = True + gui.set_hook('init', update_gui) else: - gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Tx')]) gui.unset_hook('item_changed', item_changed) gui.unset_hook('current_item_changed', recv_changed) gui.unset_hook('receive_menu', receive_menu) gui.unset_hook('update_receive_item', update_receive_item) gui.unset_hook('timer_actions', timer_actions) gui.unset_hook('close_main_window', close_main_window) - enabled = False + gui.unset_hook('init', update_gui) + + + +def update_gui(gui): + enabled = is_enabled() + if enabled: + gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Request')]) + else: + gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Tx')]) - config.set_key('pointofsale', enabled, True) toggle_QR_window(gui, enabled) - return enabled - def toggle_QR_window(self, show): if show and not self.qr_window: self.qr_window = QR_Window(self.exchanger)