From 112692498193b413c9d511292d604991e7ce98e6 Mon Sep 17 00:00:00 2001 From: ecdsa Date: Sun, 10 Mar 2013 21:36:43 +0100 Subject: [PATCH 01/10] gui plugins area --- gui/gui_classic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/gui_classic.py b/gui/gui_classic.py index b74005ad..7a8343b5 100644 --- a/gui/gui_classic.py +++ b/gui/gui_classic.py @@ -2021,7 +2021,7 @@ class ElectrumWindow(QMainWindow): # plugins if self.plugins: - tab5 = QWidget() + tab5 = QScrollArea() grid_plugins = QGridLayout(tab5) grid_plugins.setColumnStretch(0,1) tabs.addTab(tab5, _('Plugins') ) @@ -2032,9 +2032,9 @@ class ElectrumWindow(QMainWindow): name, description = p.get_info() cb = QCheckBox(name) cb.setChecked(p.is_enabled()) - cb.stateChanged.connect(mk_toggle(cb,p)) + cb.clicked.connect(mk_toggle(cb,p)) grid_plugins.addWidget(cb, i, 0) - grid_plugins.addWidget(HelpButton(description), i, 2) + grid_plugins.addWidget(HelpButton(description), i, 1) except: print_msg("Error: cannot display plugin", p) traceback.print_exc(file=sys.stdout) From 8b08eb541aa5dc682a79e1ba031b3258153610d4 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 12 Mar 2013 17:34:06 +0100 Subject: [PATCH 02/10] do not request banner twice --- lib/wallet.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/wallet.py b/lib/wallet.py index feff4fc1..ba64ad92 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -1197,9 +1197,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)) From 2de6db84e45c4db51ae693d43bd05aed10db63a6 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 12 Mar 2013 17:37:19 +0100 Subject: [PATCH 03/10] monospace font in console --- gui/qt_console.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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}) From 10bfcb918b5882b91547cefb9cd2fa3dec08b2b1 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 12 Mar 2013 17:54:26 +0100 Subject: [PATCH 04/10] replace 'ElectrumKeyID' with more general 'KeyID' field in transactions --- lib/bitcoin.py | 2 +- lib/wallet.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) 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/wallet.py b/lib/wallet.py index ba64ad92..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) From 6b9b8508d27b46a270be2d5163da812a9729959f Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 12 Mar 2013 17:55:34 +0100 Subject: [PATCH 05/10] self.run_hook --- gui/gui_classic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/gui_classic.py b/gui/gui_classic.py index ddab7973..bb9cf7ff 100644 --- a/gui/gui_classic.py +++ b/gui/gui_classic.py @@ -373,7 +373,7 @@ class ElectrumWindow(QMainWindow): if old_text: self.wallet.labels.pop(name) changed = True - run_hook('set_label', name, text, changed) + self.run_hook('set_label', name, text, changed) return changed From 06315dd9112ebbb2b8848ed674f679dd83fa98a7 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 12 Mar 2013 18:07:17 +0100 Subject: [PATCH 06/10] always pass 'self' to hooks --- gui/gui_classic.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gui/gui_classic.py b/gui/gui_classic.py index bb9cf7ff..c41113c0 100644 --- a/gui/gui_classic.py +++ b/gui/gui_classic.py @@ -357,7 +357,8 @@ 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) @@ -373,7 +374,7 @@ class ElectrumWindow(QMainWindow): if old_text: self.wallet.labels.pop(name) changed = True - self.run_hook('set_label', name, text, changed) + self.run_hook('set_label', (name, text, changed)) return changed @@ -397,14 +398,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 @@ -599,11 +600,11 @@ class ElectrumWindow(QMainWindow): 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,)) @@ -749,7 +750,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 @@ -809,7 +810,7 @@ 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.set_label(tx.hash(), label) @@ -1005,7 +1006,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)) @@ -1062,7 +1063,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 ) From 2c1ae1ba18b04ef3a98176f29f4a1c887abdbed3 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 12 Mar 2013 18:40:21 +0100 Subject: [PATCH 07/10] add a hook at the end of the gui constructor --- gui/gui_classic.py | 3 +++ plugins/pointofsale.py | 31 ++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/gui/gui_classic.py b/gui/gui_classic.py index c41113c0..e63bf0b1 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): 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) From 0c51347d9d33e789ad07c8574de237133638a2c6 Mon Sep 17 00:00:00 2001 From: ecdsa Date: Tue, 12 Mar 2013 20:28:08 +0100 Subject: [PATCH 08/10] update release notes --- RELEASE-NOTES | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 54d46b84..87f8d95c 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,14 +1,21 @@ # Release 1.7 -* The wallet does not create new addresses until transactions have 2 -confirmations. This makes recovery from seed more robust. Note that it +* The Classic GUI can be extended with plugins. Developers who want to +add new features or third-party services to Electrum are invited to +write plugins. Some previously existing and non-essential features of +Electrum (point-of-sale mode, qrcode scanner) were removed from the +core and are now available as plugins. + +* The wallet waits for 2 confirmations before creating new +addresses. This makes recovery from seed more robust. Note that it might create unwanted gaps if you use Electrum 1.7 together with older versions of Electrum. -* An interactive Python console replaces the 'wall' tab. The provided -python environment gives users access to the wallet and gui. Custom -scripts an be loaded with a "run(filename)" command. Tab-completions -are available. +* An interactive Python console replaces the 'Wall' tab. The provided +python environment gives users access to the wallet and gui. Most +electrum commands are available as python function in the +console. Custom scripts an be loaded with a "run(filename)" +command. Tab-completions are available. * The location of the Electrum folder in Windows changed from LOCALAPPDATA to APPDATA. Discussion on this topic can be found here: @@ -18,15 +25,18 @@ https://bitcointalk.org/index.php?topic=144575.0 For a single address, use the address menu (right-click). To export the keys of your entire wallet, use the settings dialog (import/export tab). -* It is possible to create, sign and redeem multisig transaction, using the command line interface. -This is made possible by the following new commands: +* It is possible to create, sign and redeem multisig transaction using the +command line interface. This is made possible by the following new commands: dumpprivkey, listunspent, createmultisig, createrawtransaction, decoderawtransaction, signrawtransaction The syntax of these commands is similar to their bitcoind counterpart. For an example, see Gavin's tutorial: https://gist.github.com/gavinandresen/3966071 -* Offline wallets do not need to be resynchronized in order to sign a transaction. - For this, use 'signrawtransaction'. ('signtx' is deprecated'). - See the help in docs/offline_wallets +* Offline wallets now work in a way similar to Armory: + 1. user creates an unsigned transaction using the online (watching-only) wallet. + 2. unsigned transaction is copied to the offline computer, and signed by the offline wallet. + 3. signed transaction is copied to the online computer, broadcasted by the online client. + +* Many command line commands have been renamed in order to make the syntax consistent with bitcoind. From 0569cc283a0f3920ba74668f9f3b3667502f608d Mon Sep 17 00:00:00 2001 From: ecdsa Date: Tue, 12 Mar 2013 20:59:37 +0100 Subject: [PATCH 09/10] fix exception --- lib/wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wallet.py b/lib/wallet.py index 2fbae844..fbd3408f 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -640,7 +640,7 @@ class Wallet: def receive_tx_callback(self, tx_hash, tx, tx_height): if not self.check_new_tx(tx_hash, tx): - raise BaseException("error: received transaction is not consistent with history"%tx_hash) + raise BaseException("error: received transaction is not consistent with history", tx_hash) with self.lock: self.transactions[tx_hash] = tx From 7740e409bea4c8897b29b86f35e711c5310b4bdc Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 12 Mar 2013 21:26:27 +0100 Subject: [PATCH 10/10] Added raw tx to the release notes --- RELEASE-NOTES | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 87f8d95c..6accaab1 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -36,12 +36,10 @@ For an example, see Gavin's tutorial: https://gist.github.com/gavinandresen/3966 2. unsigned transaction is copied to the offline computer, and signed by the offline wallet. 3. signed transaction is copied to the online computer, broadcasted by the online client. +* Raw transactions can also be loaded/signed/broadcasted via the GUI. + * Many command line commands have been renamed in order to make the syntax consistent with bitcoind. - - - - # Release 1.6.2 == Classic GUI