electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 49797c309438c56be8f1e02bc0036b04b9450e21
parent 2c67de8f642a182dd76c706b65c14eeeeee64d03
Author: Neil Booth <kyuupichan@gmail.com>
Date:   Thu,  3 Sep 2015 12:02:03 +0900

Create a Plugins class

Encapsulates plugin logic and removes global variable ugliness.

Diffstat:
Melectrum | 11++++++-----
Mgui/__init__.py | 2+-
Mgui/android.py | 78++++++++++++++++++++++++++++++++++++++----------------------------------------
Mgui/gtk.py | 76++++++++++++++++++++++++++++++++++++++--------------------------------------
Mgui/jsonrpc.py | 4++--
Mgui/qt/__init__.py | 3++-
Mgui/qt/main_window.py | 26++++++++++----------------
Mgui/stdio.py | 20++++++++++----------
Mgui/text.py | 41++++++++++++++++++++---------------------
Mlib/plugins.py | 150+++++++++++++++++++++++++++++++++++++++++++------------------------------------
10 files changed, 208 insertions(+), 203 deletions(-)

diff --git a/electrum b/electrum @@ -79,7 +79,7 @@ if is_bundle or is_local or is_android: from electrum import util from electrum import SimpleConfig, Network, Wallet, WalletStorage from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword -from electrum.plugins import init_plugins, run_hook, always_hook +from electrum.plugins import Plugins, run_hook, always_hook from electrum.commands import get_parser, known_commands, Commands, config_variables @@ -97,12 +97,12 @@ def prompt_password(prompt, confirm=True): -def init_gui(config, network): +def init_gui(config, network, plugins): gui_name = config.get('gui', 'qt') if gui_name in ['lite', 'classic']: gui_name = 'qt' gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui']) - gui = gui.ElectrumGui(config, network) + gui = gui.ElectrumGui(config, network, plugins) return gui @@ -493,9 +493,10 @@ if __name__ == '__main__': sys.exit(1) # initialize plugins. + plugins = None if not is_android: gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline' - init_plugins(config, is_bundle or is_local or is_android, gui_name) + plugins = Plugins(config, is_bundle or is_local or is_android, gui_name) # get password if needed if cmd_name not in ['gui', 'daemon']: @@ -527,7 +528,7 @@ if __name__ == '__main__': network.start() server = NetworkServer(config, network) server.start() - server.gui = init_gui(config, network) + server.gui = init_gui(config, network, plugins) server.gui.main() elif cmd_name == 'daemon': subcommand = config.get('subcommand') diff --git a/gui/__init__.py b/gui/__init__.py @@ -1,5 +1,5 @@ # To create a new GUI, please add its code to this directory. -# Two objects must be passed to the ElectrumGui: config and network +# Three objects are passed to the ElectrumGui: config, network and plugins # The Wallet object is instanciated by the GUI # Notifications about network events are sent to the GUI by using network.register_callback() diff --git a/gui/android.py b/gui/android.py @@ -126,10 +126,10 @@ def protocol_dialog(host, protocol, z): def make_layout(s, scrollable = False): content = """ - <LinearLayout + <LinearLayout android:id="@+id/zz" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:background="#ff222222"> <TextView @@ -147,13 +147,13 @@ def make_layout(s, scrollable = False): if scrollable: content = """ - <ScrollView + <ScrollView android:id="@+id/scrollview" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout - android:orientation="vertical" + android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" > @@ -167,12 +167,12 @@ def make_layout(s, scrollable = False): return """<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/background" - android:orientation="vertical" + android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="match_parent" android:background="#ff000022"> - %s + %s </LinearLayout>"""%content @@ -181,21 +181,21 @@ def make_layout(s, scrollable = False): def main_layout(): h = get_history_layout(15) l = make_layout(""" - <TextView android:id="@+id/balanceTextView" + <TextView android:id="@+id/balanceTextView" android:layout_width="match_parent" android:text="" android:textColor="#ffffffff" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceLarge" android:padding="7dip" android:textSize="8pt" android:gravity="center_vertical|center_horizontal|left"> </TextView> - <TextView android:id="@+id/historyTextView" + <TextView android:id="@+id/historyTextView" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:text="Recent transactions" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="center_vertical|center_horizontal|center"> </TextView> %s """%h,True) @@ -250,18 +250,18 @@ def qr_layout(addr, amount, message): payto_layout = make_layout(""" - <TextView android:id="@+id/recipientTextView" + <TextView android:id="@+id/recipientTextView" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:text="Pay to:" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="left"> </TextView> <EditText android:id="@+id/recipient" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:tag="Tag Me" android:inputType="text"> </EditText> @@ -275,31 +275,31 @@ payto_layout = make_layout(""" </LinearLayout> - <TextView android:id="@+id/labelTextView" + <TextView android:id="@+id/labelTextView" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:text="Message:" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="left"> </TextView> <EditText android:id="@+id/message" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:tag="Tag Me" android:inputType="text"> </EditText> - <TextView android:id="@+id/amountLabelTextView" + <TextView android:id="@+id/amountLabelTextView" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:text="Amount:" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="left"> </TextView> <EditText android:id="@+id/amount" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content" android:tag="Tag Me" android:inputType="numberDecimal"> </EditText> @@ -312,7 +312,7 @@ payto_layout = make_layout(""" settings_layout = make_layout(""" <ListView - android:id="@+id/myListView" + android:id="@+id/myListView" android:layout_width="match_parent" android:layout_height="wrap_content" />""") @@ -349,23 +349,23 @@ def get_history_layout(n): rows += """ <TableRow> <TextView - android:id="@+id/hl_%d_col1" + android:id="@+id/hl_%d_col1" android:layout_column="0" android:text="%s" android:textColor="%s" android:padding="3" /> <TextView - android:id="@+id/hl_%d_col2" + android:id="@+id/hl_%d_col2" android:layout_column="1" android:text="%s" android:padding="3" /> <TextView - android:id="@+id/hl_%d_col3" + android:id="@+id/hl_%d_col3" android:layout_column="2" android:text="%s" android:padding="3" /> <TextView - android:id="@+id/hl_%d_col4" + android:id="@+id/hl_%d_col4" android:layout_column="3" android:text="%s" android:padding="4" /> @@ -411,7 +411,7 @@ def update_layout(): text = "Synchronizing..." else: c, u, x = wallet.get_balance() - text = "Balance:"+format_satoshis(c) + text = "Balance:"+format_satoshis(c) if u: text += ' [' + format_satoshis(u,True).strip() + ']' if x: @@ -507,7 +507,7 @@ def main_loop(): event = droid.eventWait(1000).result if event is None: - if do_refresh: + if do_refresh: update_layout() do_refresh = False continue @@ -522,7 +522,7 @@ def main_loop(): if event["data"]["key"] == '4': if quitting: out = 'quit' - else: + else: quitting = True else: quitting = False @@ -555,7 +555,7 @@ def main_loop(): out = None return out - + def payto_loop(): global recipient @@ -621,7 +621,7 @@ def payto_loop(): else: modal_dialog('Error','cannot parse QR code\n'+data) - + elif event["name"] in menu_commands: out = event["name"] @@ -729,7 +729,7 @@ def show_seed(): if not password: return else: password = None - + try: seed = wallet.get_mnemonic(password) except Exception: @@ -892,7 +892,7 @@ def make_bitmap(data): finally: droid.dialogDismiss() - + droid = android.Android() @@ -904,7 +904,7 @@ config = None class ElectrumGui: - def __init__(self, _config, _network): + def __init__(self, _config, _network, plugins): global wallet, network, contacts, config network = _network config = _config @@ -912,7 +912,7 @@ class ElectrumGui: network.register_callback('connected', update_callback) network.register_callback('disconnected', update_callback) network.register_callback('disconnecting', update_callback) - + contacts = util.StoreDict(config, 'contacts') storage = WalletStorage(config.get_wallet_path()) @@ -1018,5 +1018,3 @@ class ElectrumGui: else: seed = modal_input('Mnemonic', 'please enter your code') return str(seed) - - diff --git a/gui/gtk.py b/gui/gtk.py @@ -69,8 +69,8 @@ def show_seed_dialog(seed, parent): dialog = Gtk.MessageDialog( parent = parent, - flags = Gtk.DialogFlags.MODAL, - buttons = Gtk.ButtonsType.OK, + flags = Gtk.DialogFlags.MODAL, + buttons = Gtk.ButtonsType.OK, message_format = "Your wallet generation seed is:\n\n" + '"' + seed + '"'\ + "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" ) dialog.set_title("Seed") @@ -80,9 +80,9 @@ def show_seed_dialog(seed, parent): def restore_create_dialog(): - # ask if the user wants to create a new wallet, or recover from a seed. + # ask if the user wants to create a new wallet, or recover from a seed. # if he wants to recover, and nothing is found, do not create wallet - dialog = Gtk.Dialog("electrum", parent=None, + dialog = Gtk.Dialog("electrum", parent=None, flags=Gtk.DialogFlags.MODAL, buttons= ("create", 0, "restore",1, "cancel",2) ) @@ -102,7 +102,7 @@ def run_recovery_dialog(): message = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet." dialog = Gtk.MessageDialog( parent = None, - flags = Gtk.DialogFlags.MODAL, + flags = Gtk.DialogFlags.MODAL, buttons = Gtk.ButtonsType.OK_CANCEL, message_format = message) @@ -121,7 +121,7 @@ def run_recovery_dialog(): seed_box.pack_start(seed_entry, False, False, 10) add_help_button(seed_box, '.') seed_box.show() - vbox.pack_start(seed_box, False, False, 5) + vbox.pack_start(seed_box, False, False, 5) dialog.show() r = dialog.run() @@ -142,10 +142,10 @@ def run_recovery_dialog(): def run_settings_dialog(self): message = "Here are the settings of your wallet. For more explanations, click on the question mark buttons next to each input field." - + dialog = Gtk.MessageDialog( parent = self.window, - flags = Gtk.DialogFlags.MODAL, + flags = Gtk.DialogFlags.MODAL, buttons = Gtk.ButtonsType.OK_CANCEL, message_format = message) @@ -171,7 +171,7 @@ def run_settings_dialog(self): add_help_button(fee, 'Fee per kilobyte of transaction. Recommended value:0.0001') fee.show() vbox.pack_start(fee, False,False, 5) - + nz = Gtk.HBox() nz_entry = Gtk.Entry() nz_label = Gtk.Label(label='Display zeros:') @@ -185,12 +185,12 @@ def run_settings_dialog(self): add_help_button(nz, "Number of zeros displayed after the decimal point.\nFor example, if this number is 2, then '5.' is displayed as '5.00'") nz.show() vbox.pack_start(nz, False,False, 5) - + dialog.show() r = dialog.run() fee = fee_entry.get_text() nz = nz_entry.get_text() - + dialog.destroy() if r==Gtk.ResponseType.CANCEL: return @@ -238,7 +238,7 @@ def run_network_dialog( network, parent ): dialog.set_title("Server") dialog.set_image(image) image.show() - + vbox = dialog.vbox host_box = Gtk.HBox() host_label = Gtk.Label(label='Connect to:') @@ -291,11 +291,11 @@ def run_network_dialog( network, parent ): combobox.connect("changed", lambda x:set_protocol('tshg'[combobox.get_active()])) if network.is_connected(): set_combobox(protocol) - + server_list = Gtk.ListStore(str) for host in servers.keys(): server_list.append([host]) - + treeview = Gtk.TreeView(model=server_list) treeview.show() @@ -354,8 +354,8 @@ def run_network_dialog( network, parent ): def show_message(message, parent=None): dialog = Gtk.MessageDialog( parent = parent, - flags = Gtk.DialogFlags.MODAL, - buttons = Gtk.ButtonsType.CLOSE, + flags = Gtk.DialogFlags.MODAL, + buttons = Gtk.ButtonsType.CLOSE, message_format = message ) dialog.show() dialog.run() @@ -418,7 +418,7 @@ def change_password_dialog(is_encrypted, parent): new_password = password_entry.get_text() new_password2 = password2_entry.get_text() dialog.destroy() - if result == Gtk.ResponseType.CANCEL: + if result == Gtk.ResponseType.CANCEL: return if new_password != new_password2: @@ -480,7 +480,7 @@ class ElectrumWindow: self.create_about_tab() self.notebook.show() vbox.pack_start(self.notebook, True, True, 2) - + self.status_bar = Gtk.Statusbar() vbox.pack_start(self.status_bar, False, False, 0) @@ -578,7 +578,7 @@ class ElectrumWindow: if to_address: s = r + ' <' + to_address + '>' GObject.idle_add( lambda: self.payto_entry.set_text(s) ) - + thread.start_new_thread(update_status_bar_thread, ()) if self.wallet.seed: @@ -618,7 +618,7 @@ class ElectrumWindow: def create_send_tab(self): - + page = vbox = Gtk.VBox() page.show() @@ -678,7 +678,7 @@ class ElectrumWindow: payto_sig_id = Gtk.Label(label='') payto_sig.pack_start(payto_sig_id, False, False, 0) vbox.pack_start(payto_sig, True, True, 5) - + self.user_fee = False @@ -709,7 +709,7 @@ class ElectrumWindow: fee_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#cc0000")) amount_entry.connect('changed', entry_changed, False) - fee_entry.connect('changed', entry_changed, True) + fee_entry.connect('changed', entry_changed, True) self.payto_entry = payto_entry self.payto_fee_entry = fee_entry @@ -776,7 +776,7 @@ class ElectrumWindow: m1 = re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r) m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r) - + if m1: to_address = self.wallet.get_alias(r, True, self.show_message, self.question) if not to_address: @@ -821,8 +821,8 @@ class ElectrumWindow: self.show_message( "This transaction requires a higher fee, or it will not be propagated by the network." ) return - - if label: + + if label: self.wallet.labels[tx.hash()] = label status, msg = self.wallet.sendtx( tx ) @@ -854,7 +854,7 @@ class ElectrumWindow: # s = "self-signed" # msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0] # self.show_message(msg) - + def treeview_key_press(self, treeview, event): c = treeview.get_cursor()[0] @@ -1134,7 +1134,7 @@ class ElectrumWindow: self.network_button.set_tooltip_text("Not connected.") text = "Not connected" - self.status_bar.pop(self.context_id) + self.status_bar.pop(self.context_id) self.status_bar.push(self.context_id, text) if self.wallet.up_to_date and self.wallet_updated: @@ -1214,7 +1214,7 @@ class ElectrumWindow: + "Transaction ID:\n" + tx_hash + "\n\n" \ + "Status: %d confirmations\n"%conf if is_mine: - if fee: + if fee: tx_details += "Amount sent: %s\n"% format_satoshis(v-fee, False) \ + "Transaction fee: %s\n"% format_satoshis(fee, False) else: @@ -1233,8 +1233,8 @@ class ElectrumWindow: def newaddress_dialog(self, w): - title = "New Contact" - dialog = Gtk.Dialog(title, parent=self.window, + title = "New Contact" + dialog = Gtk.Dialog(title, parent=self.window, flags=Gtk.DialogFlags.MODAL, buttons= ("cancel", 0, "ok",1) ) dialog.show() @@ -1260,7 +1260,7 @@ class ElectrumWindow: address.pack_start(address_entry, True, True, 0) address.show() dialog.vbox.pack_start(address, False, True, 5) - + result = dialog.run() address = address_entry.get_text() label = label_entry.get_text() @@ -1273,18 +1273,18 @@ class ElectrumWindow: else: errorDialog = Gtk.MessageDialog( parent=self.window, - flags=Gtk.DialogFlags.MODAL, - buttons= Gtk.ButtonsType.CLOSE, + flags=Gtk.DialogFlags.MODAL, + buttons= Gtk.ButtonsType.CLOSE, message_format = "Invalid address") errorDialog.show() errorDialog.run() errorDialog.destroy() - + class ElectrumGui(): - def __init__(self, config, network): + def __init__(self, config, network, plugins): self.network = network self.config = config @@ -1321,7 +1321,7 @@ class ElectrumGui(): wallet.add_seed(seed, password) wallet.create_master_keys(password) wallet.create_main_account(password) - + else: exit() else: @@ -1351,8 +1351,8 @@ class ElectrumGui(): dialog = Gtk.MessageDialog( parent = None, - flags = Gtk.DialogFlags.MODAL, - buttons = Gtk.ButtonsType.CANCEL, + flags = Gtk.DialogFlags.MODAL, + buttons = Gtk.ButtonsType.CANCEL, message_format = "Please wait..." ) dialog.show() diff --git a/gui/jsonrpc.py b/gui/jsonrpc.py @@ -36,7 +36,7 @@ class RequestHandler(SimpleJSONRPCRequestHandler): self.end_headers() def end_headers(self): - self.send_header("Access-Control-Allow-Headers", + self.send_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") self.send_header("Access-Control-Allow-Origin", "*") SimpleJSONRPCRequestHandler.end_headers(self) @@ -45,7 +45,7 @@ class RequestHandler(SimpleJSONRPCRequestHandler): class ElectrumGui: - def __init__(self, config, network): + def __init__(self, config, network, plugins): self.network = network self.config = config storage = WalletStorage(self.config.get_wallet_path()) diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py @@ -58,10 +58,11 @@ class OpenFileEventFilter(QObject): class ElectrumGui: - def __init__(self, config, network): + def __init__(self, config, network, plugins): set_language(config.get('language')) self.network = network self.config = config + self.plugins = plugins self.windows = [] self.efilter = OpenFileEventFilter(self.windows) self.app = QApplication(sys.argv) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -2811,12 +2811,12 @@ class ElectrumWindow(QMainWindow): def plugins_dialog(self): - from electrum.plugins import plugins, descriptions, is_available, loader - self.pluginsdialog = d = QDialog(self) d.setWindowTitle(_('Electrum Plugins')) d.setModal(1) + plugins = self.gui_object.plugins + vbox = QVBoxLayout(d) # plugins @@ -2828,40 +2828,34 @@ class ElectrumWindow(QMainWindow): w = QWidget() scroll.setWidget(w) - w.setMinimumHeight(len(plugins)*35) + w.setMinimumHeight(plugins.count() * 35) grid = QGridLayout() grid.setColumnStretch(0,1) w.setLayout(grid) def do_toggle(cb, name, w): - p = plugins.get(name) + p = plugins.toggle_enabled(self.config, name) if p: - p.disable() - p.close() - plugins.pop(name) - else: - module = loader(name) - plugins[name] = p = module.Plugin(self.config, name) - p.enable() + # FIXME: this is hosed for multiple windows p.wallet = self.wallet p.load_wallet(self.wallet, self) p.init_qt(self.gui_object) - r = p.is_enabled() - cb.setChecked(r) - if w: w.setEnabled(r) + enabled = p is not None + cb.setChecked(enabled) + if w: w.setEnabled(enabled) def mk_toggle(cb, name, w): return lambda: do_toggle(cb, name, w) - for i, descr in enumerate(descriptions): + for i, descr in enumerate(plugins.descriptions): name = descr['name'] p = plugins.get(name) if descr.get('registers_wallet_type'): continue try: cb = QCheckBox(descr['fullname']) - cb.setEnabled(is_available(name, self.wallet)) + cb.setEnabled(plugins.is_available(name, self.wallet)) cb.setChecked(p is not None and p.is_enabled()) grid.addWidget(cb, i, 0) if p and p.requires_settings(): diff --git a/gui/stdio.py b/gui/stdio.py @@ -12,7 +12,7 @@ import sys, getpass, datetime class ElectrumGui: - def __init__(self, config, network): + def __init__(self, config, network, plugins): self.network = network self.config = config storage = WalletStorage(config.get_wallet_path()) @@ -33,7 +33,7 @@ class ElectrumGui: self.wallet = Wallet(storage) self.wallet.start_threads(network) self.contacts = StoreDict(self.config, 'contacts') - + self.wallet.network.register_callback('updated', self.updated) self.wallet.network.register_callback('connected', self.connected) self.wallet.network.register_callback('disconnected', self.disconnected) @@ -97,7 +97,7 @@ class ElectrumGui: delta = (80 - sum(width) - 4)/3 format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%" \ + "%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s" - b = 0 + b = 0 messages = [] for item in self.wallet.get_history(): @@ -123,7 +123,7 @@ class ElectrumGui: if self.wallet.network.is_connected(): if not self.wallet.up_to_date: msg = _( "Synchronizing..." ) - else: + else: c, u, x = self.wallet.get_balance() msg = _("Balance")+": %f "%(Decimal(c) / COIN) if u: @@ -132,7 +132,7 @@ class ElectrumGui: msg += " [%f unmatured]"%(Decimal(x) / COIN) else: msg = _( "Not connected" ) - + return(msg) @@ -169,7 +169,7 @@ class ElectrumGui: msg = list[i] if i < len(list) else "" print(msg) - + def main(self): while self.done == 0: self.main_command() @@ -205,8 +205,8 @@ class ElectrumGui: except Exception as e: print(str(e)) return - - if self.str_description: + + if self.str_description: self.wallet.labels[tx.hash()] = self.str_description h = self.wallet.send_tx(tx) @@ -232,7 +232,7 @@ class ElectrumGui: def password_dialog(self): return getpass.getpass() - + # XXX unused @@ -240,6 +240,6 @@ class ElectrumGui: #if c == 10: # out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"]) return - + def run_contacts_tab(self, c): pass diff --git a/gui/text.py b/gui/text.py @@ -12,7 +12,7 @@ import tty, sys class ElectrumGui: - def __init__(self, config, network): + def __init__(self, config, network, plugins): self.config = config self.network = network @@ -51,8 +51,8 @@ class ElectrumGui: self.str_amount = "" self.str_fee = "" self.history = None - - if self.network: + + if self.network: self.network.register_callback('updated', self.update) self.network.register_callback('connected', self.refresh) self.network.register_callback('disconnected', self.refresh) @@ -73,7 +73,7 @@ class ElectrumGui: def verify_seed(self): pass - + def get_string(self, y, x): self.set_cursor(1) curses.echo() @@ -85,7 +85,7 @@ class ElectrumGui: def update(self): self.update_history() - if self.tab == 0: + if self.tab == 0: self.print_history() self.refresh() @@ -105,7 +105,7 @@ class ElectrumGui: delta = (self.maxx - sum(width) - 4)/3 format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%"+"%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s" - b = 0 + b = 0 self.history = [] for item in self.wallet.get_history(): @@ -130,7 +130,7 @@ class ElectrumGui: elif self.network.is_connected(): if not self.wallet.up_to_date: msg = _("Synchronizing...") - else: + else: c, u, x = self.wallet.get_balance() msg = _("Balance")+": %f "%(Decimal(c) / COIN) if u: @@ -139,12 +139,12 @@ class ElectrumGui: msg += " [%f unmatured]"%(Decimal(x) / COIN) else: msg = _("Not connected") - + self.stdscr.addstr( self.maxy -1, 3, msg) for i in range(self.num_tabs): self.stdscr.addstr( 0, 2 + 2*i + len(''.join(self.tab_names[0:i])), ' '+self.tab_names[i]+' ', curses.A_BOLD if self.tab == i else 0) - + self.stdscr.addstr( self.maxy -1, self.maxx-30, ' '.join([_("Settings"), _("Network"), _("Quit")])) @@ -221,7 +221,7 @@ class ElectrumGui: def run_history_tab(self, c): if c == 10: out = self.run_popup('',["blah","foo"]) - + def edit_str(self, target, c, is_num=False): # detect backspace @@ -246,11 +246,11 @@ class ElectrumGui: elif self.pos%6==5: if c == 10: self.do_clear() - + def run_receive_tab(self, c): if c == 10: out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"]) - + def run_contacts_tab(self, c): if c == 10 and self.contacts: out = self.run_popup('Adress', ["Copy", "Pay to", "Edit label", "Delete"]).get('button') @@ -263,7 +263,7 @@ class ElectrumGui: s = self.get_string(6 + self.pos, 18) if s: self.wallet.labels[address] = s - + def run_banner_tab(self, c): self.show_message(repr(c)) pass @@ -318,8 +318,8 @@ class ElectrumGui: except Exception as e: self.show_message(str(e)) return - - if self.str_description: + + if self.str_description: self.wallet.labels[tx.hash()] = self.str_description h = self.wallet.send_tx(tx) @@ -375,7 +375,7 @@ class ElectrumGui: proxy = None self.network.set_parameters(host, port, protocol, proxy, auto_connect) - + def settings_dialog(self): @@ -396,11 +396,11 @@ class ElectrumGui: {'label':'Password', 'type':'password', 'value':''} ], buttons = 1) return out.get('Password') - + def run_dialog(self, title, items, interval=2, buttons=None, y_pos=3): self.popup_pos = 0 - + self.w = curses.newwin( 5 + len(items)*interval + (2 if buttons else 0), 50, y_pos, 5) w = self.w out = {} @@ -441,7 +441,7 @@ class ElectrumGui: if buttons: w.addstr( 5+interval*i, 10, "[ ok ]", curses.A_REVERSE if self.popup_pos%numpos==(numpos-2) else curses.color_pair(2)) w.addstr( 5+interval*i, 25, "[cancel]", curses.A_REVERSE if self.popup_pos%numpos==(numpos-1) else curses.color_pair(2)) - + w.refresh() c = self.stdscr.getch() @@ -480,10 +480,9 @@ class ElectrumGui: new_choice = choices[(j + 1)% len(choices)] item['value'] = new_choice out[item.get('label')] = item.get('value') - + elif _type == 'button': out['button'] = item.get('label') break return out - diff --git a/lib/plugins.py b/lib/plugins.py @@ -26,70 +26,93 @@ from util import * from i18n import _ from util import print_error, profiler -plugins = {} -descriptions = [] -loader = None - -def is_available(name, w): - for d in descriptions: - if d.get('name') == name: - break - else: - return False - deps = d.get('requires', []) - for dep, s in deps: - try: - __import__(dep) - except ImportError: - return False - wallet_types = d.get('requires_wallet_type') - if wallet_types: - if w.wallet_type not in wallet_types: - return False - return True - - -def plugin_loader(config, name): - global plugins - if plugins.get(name) is None: - print_error(_("Loading plugin by constructor:"), name) - p = loader(name) - plugins[name] = p.Plugin(config, name) - return plugins[name] - -@profiler -def init_plugins(config, is_local, gui_name): - global plugins, descriptions, loader - if is_local: - fp, pathname, description = imp.find_module('plugins') - electrum_plugins = imp.load_module('electrum_plugins', fp, pathname, description) - loader = lambda name: imp.load_source('electrum_plugins.' + name, os.path.join(pathname, name + '.py')) - else: - electrum_plugins = __import__('electrum_plugins') - loader = lambda name: __import__('electrum_plugins.' + name, fromlist=['electrum_plugins']) - - def register_wallet_type(name, x): - import wallet - x += (lambda: plugin_loader(config, name),) - wallet.wallet_types.append(x) +class Plugins: + + @profiler + def __init__(self, config, is_local, gui_name): + if is_local: + find = imp.find_module('plugins') + plugins = imp.load_module('electrum_plugins', *find) + self.pathname = find[1] + else: + plugins = __import__('electrum_plugins') + self.pathname = None + + self.plugins = {} + self.descriptions = plugins.descriptions + for item in self.descriptions: + name = item['name'] + if gui_name not in item.get('available_for', []): + continue + x = item.get('registers_wallet_type') + if x: + self.register_wallet_type(name, x) + if config.get('use_' + name): + self.load_plugin(config, name) - descriptions = electrum_plugins.descriptions - for item in descriptions: - name = item['name'] - if gui_name not in item.get('available_for', []): - continue - x = item.get('registers_wallet_type') - if x: - register_wallet_type(name, x) - if not config.get('use_' + name): - continue + def print_error(self, *msg): + print_error("[%s]" % self.__class__.__name__, *msg) + + def get(self, name): + return self.plugins.get(name) + + def count(self): + return len(self.plugins) + + def load_plugin(self, config, name): + full_name = 'electrum_plugins.' + name try: - p = loader(name) - plugins[name] = p.Plugin(config, name) + if self.pathname: # local + path = os.path.join(self.pathname, name + '.py') + p = imp.load_source(full_name, path) + else: + p = __import__(full_name, fromlist=['electrum_plugins']) + plugin = p.Plugin(config, name) + self.plugins[name] = plugin + self.print_error("loaded", name) + return plugin except Exception: print_msg(_("Error: cannot initialize plugin"), name) traceback.print_exc(file=sys.stdout) + return None + + def toggle_enabled(self, config, name): + p = self.get(name) + config.set_key('use_' + name, p is None, True) + if p: + self.plugins.pop(name) + p.close() + self.print_error("closed", name) + return None + return self.load_plugin(config, name) + + def is_available(self, name, w): + for d in self.descriptions: + if d.get('name') == name: + break + else: + return False + deps = d.get('requires', []) + for dep, s in deps: + try: + __import__(dep) + except ImportError: + return False + wallet_types = d.get('requires_wallet_type') + if wallet_types: + if w.wallet_type not in wallet_types: + return False + return True + + def wallet_plugin_loader(self, config, name): + if self.plugins.get(name) is None: + self.load_plugin(config, name) + return self.plugins[name] + def register_wallet_type(self, name, x): + import wallet + x += (lambda: self.wallet_plugin_loader(config, name),) + wallet.wallet_types.append(x) hook_names = set() hooks = {} @@ -157,14 +180,6 @@ class BasePlugin: def requires_settings(self): return False - def enable(self): - self.set_enabled(True) - return True - - def disable(self): - self.set_enabled(False) - return True - @hook def load_wallet(self, wallet, window): pass @@ -179,8 +194,5 @@ class BasePlugin: def is_available(self): return True - def set_enabled(self, enabled): - self.config.set_key('use_'+self.name, enabled, True) - def settings_dialog(self): pass