Create a Plugins class

Encapsulates plugin logic and removes global variable ugliness.
This commit is contained in:
Neil Booth 2015-09-03 12:02:03 +09:00
parent 2c67de8f64
commit 49797c3094
10 changed files with 208 additions and 203 deletions

View File

@ -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')

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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())

View File

@ -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)

View File

@ -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():

View File

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

View File

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

View File

@ -26,70 +26,93 @@ from util import *
from i18n import _
from util import print_error, profiler
plugins = {}
descriptions = []
loader = None
class Plugins:
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:
@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)
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:
__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)
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
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