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:
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