Browse Source

manage exchange plugins, make sure ui doesn't stall while saving seed

and numerous other small fixes.
283
qua-non 11 years ago
committed by ThomasV
parent
commit
1179a4cf9e
  1. 4
      gui/kivy/dialog.py
  2. 15
      gui/kivy/drawer.py
  3. 22
      gui/kivy/installwizard.py
  4. 3
      gui/kivy/main.kv
  5. 53
      gui/kivy/main_window.py
  6. 311
      gui/kivy/plugins/exchange_rate.py
  7. 7
      lib/util.py

4
gui/kivy/dialog.py

@ -192,7 +192,7 @@ class InfoBubble(Bubble):
anim.start(self)
def hide(self, *dt):
def hide(self, now=False):
''' Auto fade out the Bubble
'''
def on_stop(*l):
@ -205,6 +205,8 @@ class InfoBubble(Bubble):
App.get_running_app().stop()
import sys
sys.exit()
if now:
return on_stop()
anim = Animation(opacity=0, d=.25)
anim.bind(on_complete=on_stop)

15
gui/kivy/drawer.py

@ -82,11 +82,13 @@ class Drawer(StencilView):
if app.ui_mode[0] == 't':
return super(Drawer, self).on_touch_down(touch)
state = self.state
touch.ud['send_touch_down'] = False
start = 0 if state[0] == 'c' else self._hidden_widget.right
drag_area = ((self.width * self.drag_area)
if self.state[0] == 'c' else
self._hidden_widget.width)
if touch.x > drag_area:
self.width)
if touch.x not in range(int(start), int(drag_area)):
return super(Drawer, self).on_touch_down(touch)
self._touch = touch
Clock.schedule_once(self._change_touch_mode,
@ -106,8 +108,13 @@ class Drawer(StencilView):
if not touch.ud.get('in_drag_area', None):
return super(Drawer, self).on_touch_move(touch)
self._overlay_widget.x = min(self._hidden_widget.width,
max(self._overlay_widget.x + touch.dx*2, 0))
ov = self._overlay_widget
ov.x=min(self._hidden_widget.width,
max(ov.x + touch.dx*2, 0))
#_anim = Animation(x=x, duration=1/60)
#_anim.cancel_all(ov)
#_anim.start(ov)
if abs(touch.x - touch.ox) < self.scroll_distance:
return
touch.ud['send_touch_down'] = False

22
gui/kivy/installwizard.py

@ -45,10 +45,16 @@ class InstallWizard(Widget):
'''
def target():
# run your threaded function
try:
task()
except Exception as err:
Clock.schedule_once(lambda dt: app.show_error(str(err)))
# on completion hide message
Clock.schedule_once(lambda dt: app.info_bubble.hide())
Clock.schedule_once(lambda dt: app.info_bubble.hide(now=True), -1)
# call completion routine
if on_complete:
Clock.schedule_once(lambda dt: on_complete())
@ -138,14 +144,14 @@ class InstallWizard(Widget):
brainwallet = seed
msg2 = _("[color=#414141][b]"+\
msg2 = _("[color=#414141]"+\
"[b]PLEASE WRITE DOWN YOUR SEED PASS[/b][/color]"+\
"[size=9]\n\n[/size]" +\
"[color=#929292]If you ever forget your pincode, your seed" +\
" phrase will be the [color=#EB984E]"+\
"[b]only way to recover[/b][/color] your wallet. Your " +\
" [color=#EB984E][b]Bitcoins[/b][/color] will otherwise be" +\
" [color=#EB984E]lost forever![/color]")
" [color=#EB984E][b]lost forever![/b][/color]")
if wallet.imported_keys:
msg2 += "[b][color=#ff0000ff]" + _("WARNING") + "[/color]:[/b] " +\
@ -234,13 +240,13 @@ class InstallWizard(Widget):
return app.show_error(_('Passwords do not match'), duration=.5)
if mode == 'restore':
try:
wallet.save_seed(new_password)
except Exception as err:
app.show_error(str(err))
return
def on_complete(*l):
_dlg.close()
self.load_network(wallet, mode='restore')
self.waiting_dialog(lambda: wallet.save_seed(new_password),
msg=_("saving seed"),
on_complete=on_complete)
return
if not instance:
# create

3
gui/kivy/main.kv

@ -2,6 +2,7 @@
#:import _ electrum.i18n._
#:import partial functools.partial
# Custom Global Widgets
<VGridLayout@GridLayout>:
@ -27,7 +28,7 @@
<Label>
markup: True
font_name: 'data/fonts/Roboto.ttf'
font_name: 'Roboto'
font_size: '16sp'
<ListItemButton>

53
gui/kivy/main_window.py

@ -93,7 +93,7 @@ class ElectrumWindow(App):
'While trying to save value to config')
base_unit = AliasProperty(_get_bu, _set_bu, bind=('decimal_point',))
'''BTC or UBTC or ...
'''BTC or UBTC or mBTC...
:attr:`base_unit` is a `AliasProperty` defaults to the unit set in
electrum config.
@ -148,13 +148,13 @@ class ElectrumWindow(App):
self.network = network = kwargs.get('network')
self.electrum_config = config = kwargs.get('config')
# create triggers so as to minimize updation a max of 5 times a sec
# create triggers so as to minimize updation a max of 2 times a sec
self._trigger_update_status =\
Clock.create_trigger(self.update_status, .2)
Clock.create_trigger(self.update_status, .5)
self._trigger_update_console =\
Clock.create_trigger(self.update_console, .2)
Clock.create_trigger(self.update_console, .5)
self._trigger_notify_transactions = \
Clock.create_trigger(self.notify_transactions, .2)
Clock.create_trigger(self.notify_transactions, .5)
def build(self):
from kivy.lang import Builder
@ -174,6 +174,15 @@ class ElectrumWindow(App):
Window.bind(size=self.on_size,
on_keyboard=self.on_keyboard)
Window.bind(on_key_down=self.on_key_down)
# register fonts
from kivy.core.text import Label
Label.register('Roboto',
'data/fonts/Roboto.ttf',
'data/fonts/Roboto.ttf',
'data/fonts/Roboto-Bold.ttf',
'data/fonts/Roboto-Bold.ttf')
if platform == 'android':
#
Window.bind(keyboard_height=self.on_keyboard_height)
@ -261,17 +270,20 @@ class ElectrumWindow(App):
self.load_wallet(wallet)
# check and remove this load_wallet calls update_wallet no
# need for this here
#Clock.schedule_once(update_wallet)
#TODO: URI handling
#self.windows.append(w)
#if url: w.set_url(url)
#w.app = self.app
#w.connect_slots(s)
#w.update_wallet()
#self.app.exec_()
# TODO:remove properties are used instead
#Clock.schedule_interval(self.timer_actions, .5)
#TODO: remove not needed properties allow on_property events
#def timer_actions(self):
# if self.need_update.is_set():
# self.update_wallet()
# self.need_update.clear()
# run_hook('timer_actions')
def init_ui(self):
''' Initialize The Ux part of electrum. This function performs the basic
@ -326,12 +338,12 @@ class ElectrumWindow(App):
from electrum_gui.kivy.plugins.exchange_rate import Exchanger
self.exchanger = Exchanger(self)
self.exchanger.start()
quote_currency = self.electrum_config.get("currency", 'EUR')
quote_currency = self.exchanger.currency
quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
if mode == 'symbol':
if quote_currency:
quote_currency = self.exchanger.symbols[quote_currency]
if quote_currency and mode == 'symbol':
quote_currency = self.exchanger.symbols.get(quote_currency,
quote_currency)
if quote_balance is None:
quote_text = ""
@ -340,8 +352,9 @@ class ElectrumWindow(App):
return quote_text
def set_currencies(self, quote_currencies):
self._trigger_update_status
#self.currencies = sorted(quote_currencies.keys())
#TODO remove this and just directly update a observable property
self._trigger_update_status()
self.currencies = sorted(quote_currencies.keys())
def update_console(self, *dt):
if self.console:
@ -399,7 +412,7 @@ class ElectrumWindow(App):
#if quote:
# text += " (%s)"%quote
self.notify(_("Balance: ") + text)
#self.notify(_("Balance: ") + text)
#icon = QIcon(":icons/status_connected.png")
else:
text = _("Not connected")

311
gui/kivy/plugins/exchange_rate.py

@ -2,109 +2,240 @@
'''Module exchange_rate:
This module is responsible for getting the conversion rates between different
currencies.
This module is responsible for getting the conversion rates from different
bitcoin exchanges.
'''
from kivy.network.urlrequest import UrlRequest
#kivy.event import EventDispatcher
from kivy.event import EventDispatcher
from kivy.properties import (OptionProperty, StringProperty, AliasProperty,
ListProperty)
from kivy.clock import Clock
import decimal
import json
class Exchanger(object):
'''
EXCHANGES = ["BitcoinAverage",
"BitcoinVenezuela",
"BitPay",
"Blockchain",
"BTCChina",
"CaVirtEx",
"Coinbase",
"CoinDesk",
"LocalBitcoins",
"Winkdex"]
class Exchanger(EventDispatcher):
''' Provide exchanges rate between crypto and different national
currencies. See Module Documentation for details.
'''
symbols = {'ALL': 'Lek', 'AED': 'د.إ', 'AFN':'؋', 'ARS': '$', 'AMD': '֏',
'AWG': 'ƒ', 'ANG': 'ƒ', 'AOA': 'Kz', 'BDT': '', 'BHD': 'BD',
'BIF': 'FBu', 'BTC': 'BTC', 'BTN': 'Nu',
'AUD': '$', 'AZN': 'ман', 'BSD': '$', 'BBD': '$', 'BYR': 'p',
'BIF': 'FBu', 'BTC': 'BTC', 'BTN': 'Nu', 'CDF': 'FC', 'CHF': 'CHF',
'CLF': 'UF', 'CLP':'$', 'CVE': '$', 'DJF':'Fdj', 'DZD': 'دج',
'AUD': '$', 'AZN': 'ман', 'BSD': '$', 'BBD': '$', 'BYR': 'p', 'CRC': '',
'BZD': 'BZ$', 'BMD': '$', 'BOB': '$b', 'BAM': 'KM', 'BWP': 'P',
'BGN': 'лв', 'BRL': 'R$', 'BND': '$', 'KHR': '', 'CAD': '$',
'KYD': '$', 'USD': '$', 'CLP': '$', 'CNY': '¥', 'COP': '$', 'CRC': '',
'ERN': 'Nfk', 'ETB': 'Br', 'KYD': '$', 'USD': '$', 'CLP': '$',
'HRK': 'kn', 'CUP':'', 'CZK': '', 'DKK': 'kr', 'DOP': 'RD$',
'XCD': '$', 'EGP': '£', 'SVC': '$' , 'EEK': 'kr', 'EUR': '',
'FKP': '£', 'FJD': '$', 'GHC': '¢', 'GIP': '£', 'GTQ': 'Q', 'GBP': '£',
'GYD': '$', 'HNL': 'L', 'HKD': '$', 'HUF': 'Ft', 'ISK': 'kr',
'INR': '', 'IDR': 'Rp', 'IRR': '', 'IMP': '£', 'ILS': '',
'INR': '', 'IDR': 'Rp', 'IRR': '', 'IMP': '£', 'ILS': '', 'COP': '$',
'JMD': 'J$', 'JPY': '¥', 'JEP': '£', 'KZT': 'лв', 'KPW': '',
'KRW': '', 'KGS': 'лв', 'LAK': '', 'LVL': 'Ls'}
'KRW': '', 'KGS': 'лв', 'LAK': '', 'LVL': 'Ls', 'CNY': '¥'}
_use_exchange = OptionProperty('Blockchain', options=EXCHANGES)
'''This is the exchange to be used for getting the currency exchange rates
'''
_currency = StringProperty('EUR')
'''internal use only
'''
def _set_currency(self, value):
exchanger = self.exchanger
if self.use_exchange == 'CoinDesk':
self._update_cd_currency(self.currency)
return
try:
self._currency = value
self.electrum_cinfig.set_key('currency', value, True)
except AttributeError:
self._currency = 'EUR'
def _get_currency(self):
try:
self._currency = self.electrum_config.get('currency', 'EUR')
except AttributeError:
pass
finally:
return self._currency
currency = AliasProperty(_get_currency, _set_currency, bind=('_currency',))
currencies = ListProperty(['EUR', 'GBP', 'USD'])
'''List of currencies supported by the current exchanger plugin.
:attr:`currencies` is a `ListProperty` default to ['Eur', 'GBP'. 'USD'].
'''
def _get_useex(self):
if not self.parent:
return self._use_exchange
self._use_exchange = self.parent.electrum_config.get('use_exchange',
'Blockchain')
return self._use_exchange
def _set_useex(self, value):
if not self.parent:
return self._use_exchange
self.parent.electrum_config.set_key('use_exchange', value, True)
self._use_exchange = value
use_exchange = AliasProperty(_get_useex, _set_useex,
bind=('_use_exchange', ))
def __init__(self, parent):
super(Exchanger, self).__init__()
self.parent = parent
self.quote_currencies = None
self.exchanges = ('BlockChain', 'Coinbase', 'CoinDesk')
try:
self.use_exchange = parent.electrum_config.get('use_exchange',
'BlockChain')
except AttributeError:
self.use_exchange = 'BlockChain'
self.currencies = self.symbols.keys()
self.exchanges = EXCHANGES
def exchange(self, btc_amount, quote_currency):
if self.quote_currencies is None:
return None
quote_currencies = self.quote_currencies.copy()
if quote_currency not in quote_currencies:
return None
if self.use_exchange == "CoinDesk":
return btc_amount * decimal.Decimal(quote_currencies[quote_currency])
def update_rate(self, dt):
''' This is called from :method:`start` every X seconds; to update the
rates for currencies for the currently selected exchange.
'''
update_rates = {
"BitcoinAverage": self.update_ba,
"BitcoinVenezuela": self.update_bv,
"BitPay": self.update_bp,
"Blockchain": self.update_bc,
"BTCChina": self.update_CNY,
"CaVirtEx": self.update_cv,
"CoinDesk": self.update_cd,
"Coinbase": self.update_cb,
"LocalBitcoins": self.update_lb,
"Winkdex": self.update_wd,
}
try:
connection = httplib.HTTPSConnection('api.coindesk.com')
connection.request("GET", "/v1/bpi/currentprice/" + str(quote_currency) + ".json")
except Exception:
return
resp = connection.getresponse()
if resp.reason == httplib.responses[httplib.NOT_FOUND]:
update_rates[self.use_exchange]()
except KeyError:
return
def update_wd(self):
def on_success(request, response):
response = json.loads(response)
quote_currencies = {'USD': 0.0}
lenprices = len(response["prices"])
usdprice = response['prices'][lenprices-1]['y']
try:
resp_rate = json.loads(resp.read())
except Exception:
return
return btc_amount * decimal.Decimal(str(resp_rate["bpi"][str(quote_currency)]["rate_float"]))
return btc_amount * decimal.Decimal(quote_currencies[quote_currency])
quote_currencies["USD"] = decimal.Decimal(usdprice)
except KeyError:
pass
self.quote_currencies = quote_currencies
self.parent.set_currencies(quote_currencies)
def check_rates(self, dt):
if self.use_exchange == 'BlockChain':
self.check_blockchain()
elif self.use_exchange == 'CoinDesk':
self.check_coindesk()
elif self.use_exchange == 'Coinbase':
self.check_coinbase()
req = UrlRequest(
url='https://winkdex.com/static/data/0_600_288.json',
on_success=on_success,
timeout=5)
def check_coindesk(self):
def update_cd_currency(self, currency):
def _lookup_rate(response, quote_id):
return decimal.Decimal(str(response[str(quote_id)]["15m"]))
def on_success(request, response):
response = json.loads(response)
quote_currencies = self.quote_currencies
quote_currencies[currency] =\
str(response['bpi'][str(currency)]['rate_float'])
self.parent.set_currencies(quote_currencies)
req = UrlRequest(
url='https://api.coindesk.com/v1/bpi/currentprice/'\
+ str(currency) + '.json',on_success=on_success, timeout=5)
def update_cd(self):
def on_success(request, response):
quote_currencies = {}
response = json.loads(response)
for cur in response:
quote_currencies[str(cur["currency"])] = 0.0
self.quote_currencies = quote_currencies
self.update_cd_currency(self.currency)
req = UrlRequest(
url='https://api.coindesk.com/v1/bpi/supported-currencies.json',
on_success=on_success,
timeout=5)
def update_cv(self):
def on_success(request, response):
response = json.loads(response)
quote_currencies = {"CAD": 0.0}
cadprice = response["last"]
try:
for r in response:
quote_currencies[r] = _lookup_rate(response, r)
quote_currencies["CAD"] = decimal.Decimal(cadprice)
self.quote_currencies = quote_currencies
except KeyError:
pass
self.parent.set_currencies(quote_currencies)
def on_failure(*args):
pass
req = UrlRequest(url='https://www.cavirtex.com/api/CAD/ticker.json',
on_success=on_success,
timeout=5)
def on_error(*args):
def update_CNY(self):
def on_success(request, response):
quote_currencies = {"CNY": 0.0}
cnyprice = response["ticker"]["last"]
try:
quote_currencies["CNY"] = decimal.Decimal(cnyprice)
self.quote_currencies = quote_currencies
except KeyError:
pass
self.parent.set_currencies(quote_currencies)
def on_redirect(*args):
req = UrlRequest(url='https://data.btcchina.com/data/ticker',
on_success=on_success,
timeout=5)
def update_bp(self):
def on_success(request, response):
quote_currencies = {}
try:
for r in response:
quote_currencies[str(r['code'])] = decimal.Decimal(r['rate'])
self.quote_currencies = quote_currencies
except KeyError:
pass
self.parent.set_currencies(quote_currencies)
req = UrlRequest(
url='https://api.coindesk.com/v1/bpi/supported-currencies.json',
req = UrlRequest(url='https://bitpay.com/api/rates',
on_success=on_success,
on_failure=on_failure,
on_error=on_error,
on_redirect=on_redirect,
timeout=5)
def check_coinbase(self):
def update_cb(self):
def _lookup_rate(response, quote_id):
return decimal.Decimal(str(response[str(quote_id)]))
@ -121,24 +252,12 @@ class Exchanger(object):
pass
self.parent.set_currencies(quote_currencies)
def on_failure(*args):
pass
def on_error(*args):
pass
def on_redirect(*args):
pass
req = UrlRequest(
url='https://coinbase.com/api/v1/currencies/exchange_rates',
on_success=on_success,
on_failure=on_failure,
on_error=on_error,
on_redirect=on_redirect,
timeout=5)
def check_blockchain(self):
def update_bc(self):
def _lookup_rate(response, quote_id):
return decimal.Decimal(str(response[str(quote_id)]["15m"]))
@ -153,27 +272,69 @@ class Exchanger(object):
pass
self.parent.set_currencies(quote_currencies)
def on_failure(*args):
req = UrlRequest(url='https://blockchain.info/ticker',
on_success=on_success,
timeout=5)
def update_lb(self):
def _lookup_rate(response, quote_id):
return decimal.Decimal(response[str(quote_id)]["rates"]["last"])
def on_success(request, response):
quote_currencies = {}
try:
for r in response:
quote_currencies[r] = _lookup_rate(response, r)
self.quote_currencies = quote_currencies
except KeyError:
pass
self.parent.set_currencies(quote_currencies)
req = UrlRequest(
url='https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/',
on_success=on_success,
timeout=5)
def on_error(*args):
def update_ba(self):
def on_success(request, response):
quote_currencies = {}
try:
for r in response:
quote_currencies[r] = decimal.Decimal(response[r][u'last'])
self.quote_currencies = quote_currencies
except TypeError:
pass
self.parent.set_currencies(quote_currencies)
req = UrlRequest(url='https://api.bitcoinaverage.com/ticker/global/all',
on_success=on_success,
timeout=5)
def on_redirect(*args):
def update_bv(self):
def on_success(request, response):
quote_currencies = {}
try:
for r in response["BTC"]:
quote_currencies[r] = decimal.Decimal(response['BTC'][r])
self.quote_currencies = quote_currencies
except KeyError:
pass
self.parent.set_currencies(quote_currencies)
req = UrlRequest(url='https://blockchain.info/ticker',
req = UrlRequest(url='https://api.bitcoinvenezuela.com/',
on_success=on_success,
on_failure=on_failure,
on_error=on_error,
on_redirect=on_redirect,
timeout=5)
def start(self):
# check every 5 seconds
self.check_rates(0)
Clock.schedule_interval(self.check_rates, 5)
# check rates every few seconds
self.update_rate(0)
# check every few seconds
Clock.unschedule(self.update_rate)
Clock.schedule_interval(self.update_rate, 20)
def stop(self):
Clock.unschedule(self.check_rates)
Clock.unschedule(self.update_rate)

7
lib/util.py

@ -142,6 +142,13 @@ def user_dir():
elif "LOCALAPPDATA" in os.environ:
return os.path.join(os.environ["LOCALAPPDATA"], "Electrum")
elif 'ANDROID_DATA' in os.environ:
try:
import jnius
env = jnius.autoclass('android.os.Environment')
_dir = env.getExternalStorageDirectory().getPath()
return _dir + '/electrum/'
except ImportError:
pass
return "/sdcard/electrum/"
else:
#raise Exception("No home directory found in environment variables.")

Loading…
Cancel
Save