treorganize files and bring code inline with current master - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
DIR commit c121c1aa4e2d20825d4eafc201144e84df3129dd
DIR parent 9938316400ad263ab9bec40e93f7e0b10c2ed9cd
HTML Author: akshayaurora <akshayaurora@gmail.com>
Date: Thu, 5 Jun 2014 06:12:29 +0530
reorganize files and bring code inline with current master
Conflicts:
lib/simple_config.py
Diffstat:
M data/fonts/Roboto-Bold.ttf | 0
A data/fonts/Roboto-Condensed.ttf | 0
A data/fonts/Roboto-Medium.ttf | 0
M gui/kivy/Makefile | 2 +-
M gui/kivy/__init__.py | 54 +++++++++++++++++++++++++++----
D gui/kivy/carousel.py | 33 -------------------------------
D gui/kivy/dialog.py | 686 -------------------------------
D gui/kivy/drawer.py | 188 -------------------------------
D gui/kivy/gridview.py | 203 -------------------------------
D gui/kivy/installwizard.py | 328 -------------------------------
M gui/kivy/main.kv | 439 ++++---------------------------
M gui/kivy/main_window.py | 1023 ++++++++++++++++++++++---------
M gui/kivy/plugins/exchange_rate.py | 97 +++++++++++++++++++++----------
M gui/kivy/qr_scanner/__init__.py | 54 +++----------------------------
M gui/kivy/qr_scanner/scanner_androi… | 9 +++++++--
D gui/kivy/qrcodewidget.py | 179 -------------------------------
D gui/kivy/screens.py | 105 -------------------------------
D gui/kivy/statusbar.py | 7 -------
D gui/kivy/textinput.py | 14 --------------
M gui/kivy/theming/light-0.png | 0
M gui/kivy/theming/light.atlas | 4 ++--
M gui/kivy/theming/light/action_bar.… | 0
A gui/kivy/theming/light/action_butt… | 0
A gui/kivy/theming/light/action_grou… | 0
A gui/kivy/theming/light/bit_logo.png | 0
M gui/kivy/theming/light/card.png | 0
M gui/kivy/theming/light/card_top.png | 0
M gui/kivy/theming/light/contact.png | 0
A gui/kivy/theming/light/contact_ava… | 0
A gui/kivy/theming/light/contact_ove… | 0
A gui/kivy/theming/light/dropdown_ba… | 0
M gui/kivy/theming/light/gear.png | 0
M gui/kivy/theming/light/logo.png | 0
M gui/kivy/theming/light/manualentry… | 0
M gui/kivy/theming/light/nfc_phone.p… | 0
A gui/kivy/theming/light/overflow_ba… | 0
A gui/kivy/theming/light/overflow_bt… | 0
M gui/kivy/theming/light/qrcode.png | 0
M gui/kivy/theming/light/settings.png | 0
M gui/kivy/theming/light/tab_btn_pre… | 0
M gui/kivy/theming/light/tab_strip.p… | 0
M gui/kivy/theming/light/wallets.png | 0
A gui/kivy/tools/blacklist.txt | 99 +++++++++++++++++++++++++++++++
A gui/kivy/tools/buildozer.spec | 172 ++++++++++++++++++++++++++++++
D gui/kivy/ui_screens/mainscreen.kv | 287 -------------------------------
A gui/kivy/ui_screens/screenreceive.… | 130 +++++++++++++++++++++++++++++++
A gui/kivy/ui_screens/screensend.kv | 187 +++++++++++++++++++++++++++++++
A gui/kivy/uix/__init__.py | 1 +
R gui/kivy/combobox.py -> gui/kivy/u… | 0
R gui/kivy/console.py -> gui/kivy/ui… | 0
A gui/kivy/uix/dialogs/__init__.py | 190 ++++++++++++++++++++++++++++++
A gui/kivy/uix/dialogs/carousel_dial… | 239 +++++++++++++++++++++++++++++++
A gui/kivy/uix/dialogs/create_restor… | 488 +++++++++++++++++++++++++++++++
A gui/kivy/uix/dialogs/installwizard… | 478 +++++++++++++++++++++++++++++++
A gui/kivy/uix/dialogs/new_contact.py | 26 ++++++++++++++++++++++++++
A gui/kivy/uix/dialogs/nfc_transacti… | 33 +++++++++++++++++++++++++++++++
A gui/kivy/uix/dialogs/qr_scanner.py | 42 +++++++++++++++++++++++++++++++
A gui/kivy/uix/drawer.py | 258 +++++++++++++++++++++++++++++++
A gui/kivy/uix/gridview.py | 205 ++++++++++++++++++++++++++++++
R gui/kivy/menus.py -> gui/kivy/uix/… | 0
A gui/kivy/uix/qrcodewidget.py | 180 +++++++++++++++++++++++++++++++
A gui/kivy/uix/screens.py | 300 +++++++++++++++++++++++++++++++
A gui/kivy/uix/ui_screens/mainscreen… | 1406 +++++++++++++++++++++++++++++++
A gui/kivy/uix/ui_screens/screenrece… | 139 ++++++++++++++++++++++++++++++
A gui/kivy/uix/ui_screens/screensend… | 232 ++++++++++++++++++++++++++++++
D gui/kivy/utils.py | 2 --
A lib/android/libiconv.so | 0
A lib/android/libzbarjni.so | 0
A lib/android/zbar.jar | 0
69 files changed, 5738 insertions(+), 2781 deletions(-)
---
DIR diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
Binary files differ.
DIR diff --git a/data/fonts/Roboto-Condensed.ttf b/data/fonts/Roboto-Condensed.ttf
Binary files differ.
DIR diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf
Binary files differ.
DIR diff --git a/gui/kivy/Makefile b/gui/kivy/Makefile
t@@ -9,7 +9,7 @@ apk:
# running pre build setup
@cp tools/buildozer.spec ../../buildozer.spec
# get aes.py
- @cd ../..; wget -4 https://raw.github.com/devrandom/slowaes/master/python/aes.py
+ @cd ../..; curl -O https://raw.github.com/devrandom/slowaes/master/python/aes.py
# rename electrum to main.py
@mv ../../electrum ../../main.py
@-if [ ! -d "../../.buildozer" ];then \
DIR diff --git a/gui/kivy/__init__.py b/gui/kivy/__init__.py
t@@ -21,7 +21,7 @@
import sys
#, time, datetime, re, threading
#from electrum.i18n import _, set_language
-#from electrum.util import print_error, print_msg, parse_url
+from electrum.util import print_error, print_msg, parse_url
#:TODO: replace this with kivy's own plugin managment
#from electrum.plugins import run_hook
t@@ -42,9 +42,8 @@ from kivy.logger import Logger
from electrum.bitcoin import MIN_RELAY_TX_FEE
-#:TODO main window
from main_window import ElectrumWindow
-from electrum.plugins import init_plugins
+#from electrum.plugins import init_plugins
#:TODO find a equivalent method to register to `bitcoin:` uri
#: ref: http://stackoverflow.com/questions/30931/register-file-extensions-mime-types-in-linux
t@@ -60,7 +59,6 @@ from electrum.plugins import init_plugins
# return True
# return False
-
class ElectrumGui:
def __init__(self, config, network, app=None):
t@@ -74,6 +72,47 @@ class ElectrumGui:
# base
#init_plugins(self)
+ def set_url(self, url):
+ from electrum import util
+ from decimal import Decimal
+
+ try:
+ address, amount, label, message,\
+ request_url, url = util.parse_url(url)
+ except Exception:
+ self.main_window.show_error(_('Invalid bitcoin URL'))
+ return
+
+ if amount:
+ try:
+ if main_window.base_unit == 'mBTC':
+ amount = str( 1000* Decimal(amount))
+ else:
+ amount = str(Decimal(amount))
+ except Exception:
+ amount = "0.0"
+ self.main_window.show_error(_('Invalid Amount'))
+
+ if request_url:
+ try:
+ from electrum import paymentrequest
+ except:
+ self.main_window.show_error("cannot import payment request")
+ request_url = None
+
+ if not request_url:
+ self.main_window.set_send(address, amount, label, message)
+ return
+
+ def payment_request():
+ self.payment_request = paymentrequest.PaymentRequest(request_url)
+ if self.payment_request.verify():
+ Clock.schedule_once(self.main_window.payment_request_ok)
+ else:
+ Clock.schedule_once(self.main_window.payment_request_error)
+
+ threading.Thread(target=payment_request).start()
+ self.main_window.prepare_for_payment_request()
def main(self, url):
''' The main entry point of the kivy ux
t@@ -83,5 +122,7 @@ class ElectrumGui:
'''
self.main_window = w = ElectrumWindow(config=self.config,
- network=self.network)
- w.run()
+ network=self.network,
+ url=url,
+ gui_object=self)
+ w.run()
+\ No newline at end of file
DIR diff --git a/gui/kivy/carousel.py b/gui/kivy/carousel.py
t@@ -1,32 +0,0 @@
-from kivy.uix.carousel import Carousel
-from kivy.clock import Clock
-
-class Carousel(Carousel):
-
- def on_touch_move(self, touch):
- if self._get_uid('cavoid') in touch.ud:
- return
- if self._touch is not touch:
- super(Carousel, self).on_touch_move(touch)
- return self._get_uid() in touch.ud
- if touch.grab_current is not self:
- return True
- ud = touch.ud[self._get_uid()]
- direction = self.direction
- if ud['mode'] == 'unknown':
- if direction[0] in ('r', 'l'):
- distance = abs(touch.ox - touch.x)
- else:
- distance = abs(touch.oy - touch.y)
- if distance > self.scroll_distance:
- Clock.unschedule(self._change_touch_mode)
- ud['mode'] = 'scroll'
- else:
- diff = 0
- if direction[0] in ('r', 'l'):
- diff = touch.dx
- if direction[0] in ('t', 'b'):
- diff = touch.dy
-
- self._offset += diff * 1.27
- return True
-\ No newline at end of file
DIR diff --git a/gui/kivy/dialog.py b/gui/kivy/dialog.py
t@@ -1,686 +0,0 @@
-from functools import partial
-
-from kivy.app import App
-from kivy.factory import Factory
-from kivy.uix.button import Button
-from kivy.uix.bubble import Bubble
-from kivy.uix.popup import Popup
-from kivy.uix.widget import Widget
-from kivy.uix.carousel import Carousel
-from kivy.uix.tabbedpanel import TabbedPanelHeader
-from kivy.properties import (NumericProperty, StringProperty, ListProperty,
- ObjectProperty, AliasProperty, OptionProperty,
- BooleanProperty)
-
-from kivy.animation import Animation
-from kivy.core.window import Window
-from kivy.clock import Clock
-from kivy.lang import Builder
-from kivy.metrics import dp, inch
-
-#from electrum.bitcoin import is_valid
-from electrum.i18n import _
-
-# Delayed inits
-QRScanner = None
-NFCSCanner = None
-ScreenAddress = None
-decode_uri = None
-
-DEFAULT_PATH = '/tmp/'
-app = App.get_running_app()
-
-class CarouselHeader(TabbedPanelHeader):
-
- slide = NumericProperty(0)
- ''' indicates the link to carousels slide'''
-
-class AnimatedPopup(Popup):
-
- def open(self):
- self.opacity = 0
- super(AnimatedPopup, self).open()
- anim = Animation(opacity=1, d=.5).start(self)
-
- def dismiss(self):
- def on_complete(*l):
- super(AnimatedPopup, self).dismiss()
- anim = Animation(opacity=0, d=.5)
- anim.bind(on_complete=on_complete)
- anim.start(self)
-
-
-class CarouselDialog(AnimatedPopup):
- ''' A Popup dialog with a CarouselIndicator used as the content.
- '''
-
- carousel_content = ObjectProperty(None)
-
- def open(self):
- self.opacity = 0
- super(CarouselDialog, self).open()
- anim = Animation(opacity=1, d=.5).start(self)
-
- def dismiss(self):
- def on_complete(*l):
- super(CarouselDialog, self).dismiss()
- anim = Animation(opacity=0, d=.5)
- anim.bind(on_complete=on_complete)
- anim.start(self)
-
- def add_widget(self, widget, index=0):
- if isinstance(widget, Carousel):
- super(CarouselDialog, self).add_widget(widget, index)
- return
- if 'carousel_content' not in self.ids.keys():
- super(CarouselDialog, self).add_widget(widget)
- return
- self.carousel_content.add_widget(widget, index)
-
-
-
-class NFCTransactionDialog(AnimatedPopup):
-
- mode = OptionProperty('send', options=('send','receive'))
-
- scanner = ObjectProperty(None)
-
- def __init__(self, **kwargs):
- # Delayed Init
- global NFCSCanner
- if NFCSCanner is None:
- from electrum_gui.kivy.nfc_scanner import NFCScanner
- self.scanner = NFCSCanner
-
- super(NFCTransactionDialog, self).__init__(**kwargs)
- self.scanner.nfc_init()
- self.scanner.bind()
-
- def on_parent(self, instance, value):
- sctr = self.ids.sctr
- if value:
- def _cmp(*l):
- anim = Animation(rotation=2, scale=1, opacity=1)
- anim.start(sctr)
- anim.bind(on_complete=_start)
-
- def _start(*l):
- anim = Animation(rotation=350, scale=2, opacity=0)
- anim.start(sctr)
- anim.bind(on_complete=_cmp)
- _start()
- return
- Animation.cancel_all(sctr)
-
-
-class InfoBubble(Bubble):
- '''Bubble to be used to display short Help Information'''
-
- message = StringProperty(_('Nothing set !'))
- '''Message to be displayed; defaults to "nothing set"'''
-
- icon = StringProperty('')
- ''' Icon to be displayed along with the message defaults to ''
-
- :attr:`icon` is a `StringProperty` defaults to `''`
- '''
-
- fs = BooleanProperty(False)
- ''' Show Bubble in half screen mode
-
- :attr:`fs` is a `BooleanProperty` defaults to `False`
- '''
-
- modal = BooleanProperty(False)
- ''' Allow bubble to be hidden on touch.
-
- :attr:`modal` is a `BooleanProperty` defauult to `False`.
- '''
-
- exit = BooleanProperty(False)
- '''Indicates whether to exit app after bubble is closed.
-
- :attr:`exit` is a `BooleanProperty` defaults to False.
- '''
-
- dim_background = BooleanProperty(False)
- ''' Indicates Whether to draw a background on the windows behind the bubble.
-
- :attr:`dim` is a `BooleanProperty` defaults to `False`.
- '''
-
- def on_touch_down(self, touch):
- if self.modal:
- return True
- self.hide()
- if self.collide_point(*touch.pos):
- return True
-
- def show(self, pos, duration, width=None, modal=False, exit=False):
- '''Animate the bubble into position'''
- self.modal, self.exit = modal, exit
- if width:
- self.width = width
- if self.modal:
- from kivy.uix.modalview import ModalView
- self._modal_view = m = ModalView()
- Window.add_widget(m)
- m.add_widget(self)
- else:
- Window.add_widget(self)
- # wait for the bubble to adjust it's size according to text then animate
- Clock.schedule_once(lambda dt: self._show(pos, duration))
-
- def _show(self, pos, duration):
-
- def on_stop(*l):
- if duration:
- Clock.schedule_once(self.hide, duration + .5)
-
- self.opacity = 0
- arrow_pos = self.arrow_pos
- if arrow_pos[0] in ('l', 'r'):
- pos = pos[0], pos[1] - (self.height/2)
- else:
- pos = pos[0] - (self.width/2), pos[1]
-
- self.limit_to = Window
-
- anim = Animation(opacity=1, pos=pos, d=.32)
- anim.bind(on_complete=on_stop)
- anim.cancel_all(self)
- anim.start(self)
-
-
- def hide(self, now=False):
- ''' Auto fade out the Bubble
- '''
- def on_stop(*l):
- if self.modal:
- m = self._modal_view
- m.remove_widget(self)
- Window.remove_widget(m)
- Window.remove_widget(self)
- if self.exit:
- 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)
- anim.cancel_all(self)
- anim.start(self)
-
-
-class InfoContent(Widget):
- '''Abstract class to be used to add to content to InfoDialog'''
- pass
-
-
-class InfoButton(Button):
- '''Button that is auto added to the dialog when setting `buttons:`
- property.
- '''
- pass
-
-
-class EventsDialog(AnimatedPopup):
- ''' Abstract Popup that provides the following events
- .. events::
- `on_release`
- `on_press`
- '''
-
- __events__ = ('on_release', 'on_press')
-
- def __init__(self, **kwargs):
- super(EventsDialog, self).__init__(**kwargs)
- self._on_release = kwargs.get('on_release')
- Window.bind(size=self.on_size,
- rotation=self.on_size)
- self.on_size(Window, Window.size)
-
- def on_size(self, instance, value):
- if app.ui_mode[0] == 'p':
- self.size = Window.size
- else:
- #tablet
- if app.orientation[0] == 'p':
- #portrait
- self.size = Window.size[0]/1.67, Window.size[1]/1.4
- else:
- self.size = Window.size[0]/2.5, Window.size[1]
-
- def on_release(self, instance):
- pass
-
- def on_press(self, instance):
- pass
-
- def close(self):
- self._on_release = None
- self.dismiss()
-
-
-class InfoDialog(EventsDialog):
- ''' A dialog box meant to display info along with buttons at the bottom
-
- '''
-
- buttons = ListProperty([_('ok'), _('cancel')])
- '''List of Buttons to be displayed at the bottom'''
-
- def __init__(self, **kwargs):
- self._old_buttons = self.buttons
- super(InfoDialog, self).__init__(**kwargs)
- self.on_buttons(self, self.buttons)
-
- def on_buttons(self, instance, value):
- if 'buttons_layout' not in self.ids.keys():
- return
- if value == self._old_buttons:
- return
- blayout = self.ids.buttons_layout
- blayout.clear_widgets()
- for btn in value:
- ib = InfoButton(text=btn)
- ib.bind(on_press=partial(self.dispatch, 'on_press'))
- ib.bind(on_release=partial(self.dispatch, 'on_release'))
- blayout.add_widget(ib)
- self._old_buttons = value
- pass
-
- def add_widget(self, widget, index=0):
- if isinstance(widget, InfoContent):
- self.ids.info_content.add_widget(widget, index=index)
- else:
- super(InfoDialog, self).add_widget(widget)
-
-
-class TakeInputDialog(InfoDialog):
- ''' A simple Dialog for displaying a message and taking a input
- using a Textinput
- '''
-
- text = StringProperty('Nothing set yet')
-
- readonly = BooleanProperty(False)
-
-
-class EditLabelDialog(TakeInputDialog):
- pass
-
-
-
-class ImportPrivateKeysDialog(TakeInputDialog):
- pass
-
-
-
-class ShowMasterPublicKeyDialog(TakeInputDialog):
- pass
-
-
-class EditDescriptionDialog(TakeInputDialog):
-
- pass
-
-
-class PrivateKeyDialog(InfoDialog):
-
- private_key = StringProperty('')
- ''' private key to be displayed in the TextInput
- '''
-
- address = StringProperty('')
- ''' address to be displayed in the dialog
- '''
-
-
-class SignVerifyDialog(InfoDialog):
-
- address = StringProperty('')
- '''current address being verified'''
-
-
-
-class MessageBox(InfoDialog):
-
- image = StringProperty('atlas://gui/kivy/theming/light/info')
- '''path to image to be displayed on the left'''
-
- message = StringProperty('Empty Message')
- '''Message to be displayed on the dialog'''
-
- def __init__(self, **kwargs):
- super(MessageBox, self).__init__(**kwargs)
- self.title = kwargs.get('title', _('Message'))
-
-
-class MessageBoxExit(MessageBox):
-
- def __init__(self, **kwargs):
- super(MessageBox, self).__init__(**kwargs)
- self.title = kwargs.get('title', _('Exiting'))
-
-class MessageBoxError(MessageBox):
-
- def __init__(self, **kwargs):
- super(MessageBox, self).__init__(**kwargs)
- self.title = kwargs.get('title', _('Error'))
-
-
-class WalletAddressesDialog(CarouselDialog):
-
- def __init__(self, **kwargs):
- super(WalletAddressesDialog, self).__init__(**kwargs)
- CarouselHeader = Factory.CarouselHeader
- ch = CarouselHeader()
- ch.slide = 0 # idx
-
- # delayed init
- global ScreenAddress
- if not ScreenAddress:
- from electrum_gui.kivy.screens import ScreenAddress
- slide = ScreenAddress()
-
- slide.tab=ch
-
- labels = app.wallet.labels
- addresses = app.wallet.addresses()
- _labels = {}
- for address in addresses:
- _labels[labels.get(address, address)] = address
-
- slide.labels = _labels
-
- self.add_widget(slide)
- self.add_widget(ch)
- Clock.schedule_once(lambda dt: self.delayed_init(slide))
-
- def delayed_init(self, slide):
- # add a tab for each wallet
- # for wallet in wallets
- slide.ids.btn_address.values = values = slide.labels.keys()
- slide.ids.btn_address.text = values[0]
-
-
-
-class RecentActivityDialog(CarouselDialog):
-
- def send_payment(self, address):
- tabs = app.root.main_screen.ids.tabs
- screen_send = tabs.ids.screen_send
- # remove self
- self.dismiss()
- # switch_to the send screen
- tabs.ids.panel.switch_to(tabs.ids.tab_send)
- # populate
- screen_send.ids.payto_e.text = address
-
- def populate_inputs_outputs(self, app, tx_hash):
- if tx_hash:
- tx = app.wallet.transactions.get(tx_hash)
- self.ids.list_outputs.content_adapter.data = \
- [(address, app.gui.main_gui.format_amount(value))\
- for address, value in tx.outputs]
- self.ids.list_inputs.content_adapter.data = \
- [(input['address'], input['prevout_hash'])\
- for input in tx.inputs]
-
-
-class CreateAccountDialog(EventsDialog):
- ''' Abstract dialog to be used as the base for all Create Account Dialogs
- '''
- crcontent = ObjectProperty(None)
-
- def add_widget(self, widget, index=0):
- if not self.crcontent:
- super(CreateAccountDialog, self).add_widget(widget)
- else:
- self.crcontent.add_widget(widget, index=index)
-
-
-class CreateRestoreDialog(CreateAccountDialog):
- ''' Initial Dialog for creating or restoring seed'''
-
- def on_parent(self, instance, value):
- if value:
- self.ids.but_close.disabled = True
- self.ids.but_close.opacity = 0
- self._back = _back = partial(app.dispatch, 'on_back')
- app.navigation_higherarchy.append(_back)
-
- def close(self):
- if self._back in app.navigation_higherarchy:
- app.navigation_higherarchy.pop()
- self._back = None
- super(CreateRestoreDialog, self).close()
-
-
-class InitSeedDialog(CreateAccountDialog):
-
- seed_msg = StringProperty('')
- '''Text to be displayed in the TextInput'''
-
- message = StringProperty('')
- '''Message to be displayed under seed'''
-
- seed = ObjectProperty(None)
-
- def on_parent(self, instance, value):
- if value:
- stepper = self.ids.stepper
- stepper.opacity = 1
- stepper.source = 'atlas://gui/kivy/theming/light/stepper_full'
- self._back = _back = partial(self.ids.back.dispatch, 'on_release')
- app.navigation_higherarchy.append(_back)
-
- def close(self):
- if self._back in app.navigation_higherarchy:
- app.navigation_higherarchy.pop()
- self._back = None
- super(InitSeedDialog, self).close()
-
-class VerifySeedDialog(CreateAccountDialog):
-
- pass
-
-class RestoreSeedDialog(CreateAccountDialog):
-
- def on_parent(self, instance, value):
- if value:
- tis = self.ids.text_input_seed
- tis.focus = True
- tis._keyboard.bind(on_key_down=self.on_key_down)
- stepper = self.ids.stepper
- stepper.opacity = 1
- stepper.source = ('atlas://gui/kivy/theming'
- '/light/stepper_restore_seed')
- self._back = _back = partial(self.ids.back.dispatch, 'on_release')
- app.navigation_higherarchy.append(_back)
-
- def on_key_down(self, keyboard, keycode, key, modifiers):
- if keycode[0] in (13, 271):
- self.on_enter()
- return True
- #super
-
- def on_enter(self):
- #self._remove_keyboard()
- # press next
- self.ids.next.dispatch('on_release')
-
- def _remove_keyboard(self):
- tis = self.ids.text_input_seed
- if tis._keyboard:
- tis._keyboard.unbind(on_key_down=self.on_key_down)
- tis.focus = False
-
- def close(self):
- self._remove_keyboard()
- if self._back in app.navigation_higherarchy:
- app.navigation_higherarchy.pop()
- self._back = None
- super(RestoreSeedDialog, self).close()
-
-class NewContactDialog(Popup):
-
- qrscr = ObjectProperty(None)
- _decoder = None
-
- def load_qr_scanner(self):
- global QRScanner
- if not QRScanner:
- from electrum_gui.kivy.qr_scanner import QRScanner
- qrscr = self.qrscr
- if not qrscr:
- self.qrscr = qrscr = QRScanner(opacity=0)
- #pos=self.pos, size=self.size)
- #self.bind(pos=qrscr.setter('pos'),
- # size=qrscr.setter('size')
- qrscr.bind(symbols=self.on_symbols)
- bl = self.ids.bl
- bl.clear_widgets()
- bl.add_widget(qrscr)
- qrscr.opacity = 1
- Animation(height=dp(280)).start(self)
- Animation(opacity=1).start(self)
- qrscr.start()
-
- def on_symbols(self, instance, value):
- instance.stop()
- self.remove_widget(instance)
- self.ids.but_contact.dispatch('on_release')
- global decode_uri
- if not decode_uri:
- from electrum_gui.kivy.qr_scanner import decode_uri
- uri = decode_uri(value[0].data)
- self.ids.ti.text = uri.get('address', 'empty')
- self.ids.ti_lbl.text = uri.get('label', 'empty')
- self.ids.ti_lbl.focus = True
-
-
-class PasswordRequiredDialog(InfoDialog):
-
- pass
-
-
-class ChangePasswordDialog(CreateAccountDialog):
-
- message = StringProperty(_('Empty Message'))
- '''Message to be displayed.'''
-
- mode = OptionProperty('new',
- options=('new', 'confirm', 'create', 'restore'))
- ''' Defines the mode of the password dialog.'''
-
- def validate_new_password(self):
- self.ids.next.dispatch('on_release')
-
- def on_parent(self, instance, value):
- if value:
- stepper = self.ids.stepper
- stepper.opacity = 1
- t_wallet_name = self.ids.ti_wallet_name
- if self.mode in ('create', 'restore'):
- t_wallet_name.text = 'Default Wallet'
- t_wallet_name.readonly = True
- self.ids.ti_new_password.focus = True
- else:
- t_wallet_name.text = ''
- t_wallet_name.readonly = False
- t_wallet_name.focus = True
- stepper.source = 'atlas://gui/kivy/theming/light/stepper_left'
- self._back = _back = partial(self.ids.back.dispatch, 'on_release')
- app.navigation_higherarchy.append(_back)
-
- def close(self):
- ids = self.ids
- ids.ti_wallet_name.text = ""
- ids.ti_wallet_name.focus = False
- ids.ti_password.text = ""
- ids.ti_password.focus = False
- ids.ti_new_password.text = ""
- ids.ti_new_password.focus = False
- ids.ti_confirm_password.text = ""
- ids.ti_confirm_password.focus = False
- if self._back in app.navigation_higherarchy:
- app.navigation_higherarchy.pop()
- self._back = None
- super(ChangePasswordDialog, self).close()
-
-
-
-class Dialog(Popup):
-
- content_padding = NumericProperty('2dp')
- '''Padding for the content area of the dialog defaults to 2dp
- '''
-
- buttons_padding = NumericProperty('2dp')
- '''Padding for the bottns area of the dialog defaults to 2dp
- '''
-
- buttons_height = NumericProperty('40dp')
- '''Height to be used for the Buttons at the bottom
- '''
-
- def close(self):
- self.dismiss()
-
- def add_content(self, widget, index=0):
- self.ids.layout_content.add_widget(widget, index)
-
- def add_button(self, widget, index=0):
- self.ids.layout_buttons.add_widget(widget, index)
-
-
-class SaveDialog(Popup):
-
- filename = StringProperty('')
- '''The default file name provided
- '''
-
- filters = ListProperty([])
- ''' list of files to be filtered and displayed defaults to allow all
- '''
-
- path = StringProperty(DEFAULT_PATH)
- '''path to be loaded by default in this dialog
- '''
-
- file_chooser = ObjectProperty(None)
- '''link to the file chooser object inside the dialog
- '''
-
- text_input = ObjectProperty(None)
- '''
- '''
-
- cancel_button = ObjectProperty(None)
- '''
- '''
-
- save_button = ObjectProperty(None)
- '''
- '''
-
- def close(self):
- self.dismiss()
-
-
-class LoadDialog(SaveDialog):
-
- def _get_load_btn(self):
- return self.save_button
-
- load_button = AliasProperty(_get_load_btn, None, bind=('save_button', ))
- '''Alias to the Save Button to be used as LoadButton
- '''
-
- def __init__(self, **kwargs):
- super(LoadDialog, self).__init__(**kwargs)
- self.load_button.text=_("Load")
DIR diff --git a/gui/kivy/drawer.py b/gui/kivy/drawer.py
t@@ -1,187 +0,0 @@
-
-from kivy.uix.stencilview import StencilView
-from kivy.uix.boxlayout import BoxLayout
-from kivy.uix.image import Image
-
-from kivy.animation import Animation
-from kivy.clock import Clock
-from kivy.properties import OptionProperty, NumericProperty, ObjectProperty
-
-# delayed import
-app = None
-
-
-class Drawer(StencilView):
-
- state = OptionProperty('closed',
- options=('closed', 'open', 'opening', 'closing'))
- '''This indicates the current state the drawer is in.
-
- :attr:`state` is a `OptionProperty` defaults to `closed`. Can be one of
- `closed`, `open`, `opening`, `closing`.
- '''
-
- scroll_timeout = NumericProperty(200)
- '''Timeout allowed to trigger the :data:`scroll_distance`,
- in milliseconds. If the user has not moved :data:`scroll_distance`
- within the timeout, the scrolling will be disabled and the touch event
- will go to the children.
-
- :data:`scroll_timeout` is a :class:`~kivy.properties.NumericProperty`
- and defaults to 200 (milliseconds)
- '''
-
- scroll_distance = NumericProperty('9dp')
- '''Distance to move before scrolling the :class:`Drawer` in pixels.
- As soon as the distance has been traveled, the :class:`Drawer` will
- start to scroll, and no touch event will go to children.
- It is advisable that you base this value on the dpi of your target
- device's screen.
-
- :data:`scroll_distance` is a :class:`~kivy.properties.NumericProperty`
- and defaults to 20dp.
- '''
-
- drag_area = NumericProperty(.1)
- '''The percentage of area on the left edge that triggers the opening of
- the drawer. from 0-1
-
- :attr:`drag_area` is a `NumericProperty` defaults to 2
- '''
-
- _hidden_widget = ObjectProperty(None)
- _overlay_widget = ObjectProperty(None)
-
- def __init__(self, **kwargs):
- super(Drawer, self).__init__(**kwargs)
- self.bind(pos=self._do_layout,
- size=self._do_layout,
- children=self._do_layout)
-
- def _do_layout(self, instance, value):
- if not self._hidden_widget or not self._overlay_widget:
- return
- self._overlay_widget.height = self._hidden_widget.height =\
- self.height
-
- def on_touch_down(self, touch):
- if self.disabled:
- return
-
- if not self.collide_point(*touch.pos):
- return
-
- touch.grab(self)
-
- global app
- if not app:
- from kivy.app import App
- app = App.get_running_app()
-
- # skip on tablet mode
- 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.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,
- self.scroll_timeout/1000.)
- touch.ud['in_drag_area'] = True
- touch.ud['send_touch_down'] = True
- return
-
- def on_touch_move(self, touch):
- if not touch.grab_current:
- return
-
- # skip on tablet mode
- if app.ui_mode[0] == 't':
- return super(Drawer, self).on_touch_move(touch)
-
- if not touch.ud.get('in_drag_area', None):
- return super(Drawer, self).on_touch_move(touch)
-
- ov = self._overlay_widget
- ov.x=min(self._hidden_widget.width,
- max(ov.x + touch.dx*2, 0))
- #_anim = Animation(x=x, duration=1/2, t='in_out_quart')
- #_anim.cancel_all(ov)
- #_anim.start(ov)
-
- if abs(touch.x - touch.ox) < self.scroll_distance:
- return
- touch.ud['send_touch_down'] = False
- Clock.unschedule(self._change_touch_mode)
- self._touch = None
- self.state = 'opening' if touch.dx > 0 else 'closing'
- touch.ox = touch.x
- return
-
- def _change_touch_mode(self, *args):
- if not self._touch:
- return
- touch = self._touch
- touch.ud['in_drag_area'] = False
- touch.ud['send_touch_down'] = False
- self._touch = None
- super(Drawer, self).on_touch_down(touch)
- return
-
- def on_touch_up(self, touch):
- if not touch.grab_current:
- return
-
- # skip on tablet mode
- if app.ui_mode[0] == 't':
- return super(Drawer, self).on_touch_down(touch)
-
- if touch.ud.get('send_touch_down', None):
- Clock.unschedule(self._change_touch_mode)
- Clock.schedule_once(
- lambda dt: super(Drawer, self).on_touch_down(touch), -1)
- if touch.ud.get('in_drag_area', None):
- touch.ud['in_drag_area'] = False
- Animation.cancel_all(self._overlay_widget)
- anim = Animation(x=self._hidden_widget.width
- if self.state[0] == 'o' else 0,
- d=.1, t='linear')
- anim.bind(on_complete = self._complete_drawer_animation)
- anim.start(self._overlay_widget)
- Clock.schedule_once(
- lambda dt: super(Drawer, self).on_touch_up(touch), 0)
-
- def _complete_drawer_animation(self, *args):
- self.state = 'open' if self.state[0] == 'o' else 'closed'
-
- def add_widget(self, widget, index=1):
- if not widget:
- return
- children = self.children
- len_children = len(children)
- if len_children == 2:
- Logger.debug('Drawer: No more than two widgets allowed')
- return
-
- super(Drawer, self).add_widget(widget)
- if len_children == 0:
- # first widget add it to the hidden/drawer section
- self._hidden_widget = widget
- return
- # Second Widget
- self._overlay_widget = widget
-
- def remove_widget(self, widget):
- super(Drawer, self).remove_widget(self)
- if widget == self._hidden_widget:
- self._hidden_widget = None
- return
- if widget == self._overlay_widget:
- self._overlay_widget = None
- return
-\ No newline at end of file
DIR diff --git a/gui/kivy/gridview.py b/gui/kivy/gridview.py
t@@ -1,203 +0,0 @@
-from kivy.uix.boxlayout import BoxLayout
-from kivy.adapters.dictadapter import DictAdapter
-from kivy.adapters.listadapter import ListAdapter
-from kivy.properties import ObjectProperty, ListProperty, AliasProperty
-from kivy.uix.listview import (ListItemButton, ListItemLabel, CompositeListItem,
- ListView)
-from kivy.lang import Builder
-from kivy.metrics import dp, sp
-
-Builder.load_string('''
-<GridView>
- header_view: header_view
- content_view: content_view
- BoxLayout:
- orientation: 'vertical'
- padding: '0dp', '2dp'
- BoxLayout:
- id: header_box
- orientation: 'vertical'
- size_hint: 1, None
- height: '30dp'
- ListView:
- id: header_view
- BoxLayout:
- id: content_box
- orientation: 'vertical'
- ListView:
- id: content_view
-
-<-HorizVertGrid>
- header_view: header_view
- content_view: content_view
- ScrollView:
- id: scrl
- do_scroll_y: False
- RelativeLayout:
- size_hint_x: None
- width: max(scrl.width, dp(sum(root.widths)))
- BoxLayout:
- orientation: 'vertical'
- padding: '0dp', '2dp'
- BoxLayout:
- id: header_box
- orientation: 'vertical'
- size_hint: 1, None
- height: '30dp'
- ListView:
- id: header_view
- BoxLayout:
- id: content_box
- orientation: 'vertical'
- ListView:
- id: content_view
-
-''')
-
-class GridView(BoxLayout):
- """Workaround solution for grid view by using 2 list view.
- Sometimes the height of lines is shown properly."""
-
- def _get_hd_adpt(self):
- return self.ids.header_view.adapter
-
- header_adapter = AliasProperty(_get_hd_adpt, None)
- '''
- '''
-
- def _get_cnt_adpt(self):
- return self.ids.content_view.adapter
-
- content_adapter = AliasProperty(_get_cnt_adpt, None)
- '''
- '''
-
- headers = ListProperty([])
- '''
- '''
-
- widths = ListProperty([])
- '''
- '''
-
- data = ListProperty([])
- '''
- '''
-
- getter = ObjectProperty(lambda item, i: item[i])
- '''
- '''
- on_context_menu = ObjectProperty(None)
-
- def __init__(self, **kwargs):
- super(GridView, self).__init__(**kwargs)
- self._from_widths = False
- #self.on_headers(self, self.headers)
-
- def on_widths(self, instance, value):
- self._from_widths = True
- self.on_headers(instance, self.headers)
- self._from_widths = False
-
- def on_headers(self, instance, value):
- if not self._from_widths:
- return
- if not (value and self.canvas and self.headers):
- return
- widths = self.widths
- if len(self.widths) != len(value):
- return
- #if widths is not None:
- # widths = ['%sdp' % i for i in widths]
-
- def generic_args_converter(row_index,
- item,
- is_header=True,
- getter=self.getter):
- cls_dicts = []
- _widths = self.widths
- getter = self.getter
- on_context_menu = self.on_context_menu
-
- for i, header in enumerate(self.headers):
- kwargs = {
- 'padding': ('2dp','2dp'),
- 'halign': 'center',
- 'valign': 'middle',
- 'size_hint_y': None,
- 'shorten': True,
- 'height': '30dp',
- 'text_size': (_widths[i], dp(30)),
- 'text': getter(item, i),
- }
-
- kwargs['font_size'] = '9sp'
- if is_header:
- kwargs['deselected_color'] = kwargs['selected_color'] =\
- [0, 1, 1, 1]
- else: # this is content
- kwargs['deselected_color'] = 1, 1, 1, 1
- if on_context_menu is not None:
- kwargs['on_press'] = on_context_menu
-
- if widths is not None: # set width manually
- kwargs['size_hint_x'] = None
- kwargs['width'] = widths[i]
-
- cls_dicts.append({
- 'cls': ListItemButton,
- 'kwargs': kwargs,
- })
-
- return {
- 'id': item[-1],
- 'size_hint_y': None,
- 'height': '30dp',
- 'cls_dicts': cls_dicts,
- }
-
- def header_args_converter(row_index, item):
- return generic_args_converter(row_index, item)
-
- def content_args_converter(row_index, item):
- return generic_args_converter(row_index, item, is_header=False)
-
-
- self.ids.header_view.adapter = ListAdapter(data=[self.headers],
- args_converter=header_args_converter,
- selection_mode='single',
- allow_empty_selection=False,
- cls=CompositeListItem)
-
- self.ids.content_view.adapter = ListAdapter(data=self.data,
- args_converter=content_args_converter,
- selection_mode='single',
- allow_empty_selection=False,
- cls=CompositeListItem)
- self.content_adapter.bind_triggers_to_view(self.ids.content_view._trigger_reset_populate)
-
-class HorizVertGrid(GridView):
- pass
-
-
-if __name__ == "__main__":
- from kivy.app import App
- class MainApp(App):
-
- def build(self):
- data = []
- for i in range(90):
- data.append((str(i), str(i)))
- self.data = data
- return Builder.load_string('''
-BoxLayout:
- orientation: 'vertical'
- HorizVertGrid:
- on_parent: if args[1]: self.content_adapter.data = app.data
- headers:['Address', 'Previous output']
- widths: [400, 500]
-
-<Label>
- font_size: '16sp'
-''')
- MainApp().run()
DIR diff --git a/gui/kivy/installwizard.py b/gui/kivy/installwizard.py
t@@ -1,328 +0,0 @@
-from electrum import Wallet
-from electrum.i18n import _
-
-from kivy.app import App
-from kivy.uix.widget import Widget
-from kivy.core.window import Window
-from kivy.clock import Clock
-
-from electrum_gui.kivy.dialog import CreateRestoreDialog
-#from network_dialog import NetworkDialog
-#from util import *
-#from amountedit import AmountEdit
-
-import sys
-import threading
-from functools import partial
-
-# global Variables
-app = App.get_running_app()
-
-
-class InstallWizard(Widget):
- '''Installation Wizard. Responsible for instantiating the
- creation/restoration of wallets.
-
- events::
- `on_wizard_complete` Fired when the wizard is done creating/ restoring
- wallet/s.
- '''
-
- __events__ = ('on_wizard_complete', )
-
- def __init__(self, config, network, storage):
- super(InstallWizard, self).__init__()
- self.config = config
- self.network = network
- self.storage = storage
-
- def waiting_dialog(self, task,
- msg= _("Electrum is generating your addresses,"
- " please wait."),
- on_complete=None):
- '''Perform a blocking task in the background by running the passed
- method in a thread.
- '''
-
- 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(now=True), -1)
-
- # call completion routine
- if on_complete:
- Clock.schedule_once(lambda dt: on_complete())
-
- app.show_info_bubble(
- text=msg, icon='atlas://gui/kivy/theming/light/important',
- pos=Window.center, width='200sp', arrow_pos=None, modal=True)
- t = threading.Thread(target = target)
- t.start()
-
- def run(self):
- '''Entry point of our Installation wizard
- '''
- CreateRestoreDialog(on_release=self.on_creatrestore_complete).open()
-
- def on_creatrestore_complete(self, dialog, button):
- if not button:
- return self.dispatch('on_wizard_complete', None)
-
- #gap = self.config.get('gap_limit', 5)
- #if gap !=5:
- # wallet.gap_limit = gap_limit
- # wallet.storage.put('gap_limit', gap, True)
-
- dialog.close()
- if button == dialog.ids.create:
- # create
- wallet = Wallet(self.storage)
- self.change_password_dialog(wallet=wallet)
- elif button == dialog.ids.restore:
- # restore
- wallet = None
- self.restore_seed_dialog(wallet)
- #if button == dialog.ids.watching:
- #TODO: not available in the new design
- # self.action = 'watching'
- else:
- self.dispatch('on_wizard_complete', None)
-
- def restore_seed_dialog(self, wallet):
- from electrum_gui.kivy.dialog import RestoreSeedDialog
- RestoreSeedDialog(
- on_release=partial(self.on_verify_restore_ok, wallet)).open()
-
- def on_verify_restore_ok(self, wallet, _dlg, btn, restore=False):
-
- if _dlg.ids.back == btn:
- _dlg.close()
- CreateRestoreDialog(
- on_release=self.on_creatrestore_complete).open()
- return
-
- seed = unicode(_dlg.ids.text_input_seed.text)
- if not seed:
- app.show_error(_("No seed!"), duration=.5)
- return
-
- try:
- wallet = Wallet.from_seed(seed, self.storage)
- except Exception as err:
- _dlg.close()
- return app.show_error(str(err) + '\n App will now exit',
- exit=True, modal=True, duration=.5)
- _dlg.close()
- return self.change_password_dialog(wallet=wallet, mode='restore')
-
-
- def init_seed_dialog(self, wallet=None, instance=None, password=None,
- wallet_name=None, mode='create'):
- # renamed from show_seed()
- '''Can be called directly (password is None)
- or from a password-protected callback (password is not None)'''
-
- if not wallet or not wallet.seed:
- if instance == None:
- wallet.init_seed(None)
- else:
- return app.show_error(_('No seed'))
-
- if password is None or not instance:
- seed = wallet.get_mnemonic(None)
- else:
- try:
- seed = self.wallet.get_seed(password)
- except Exception:
- return app.show_error(_('Incorrect Password'))
-
- brainwallet = seed
-
- 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][b]lost forever![/b][/color]")
-
- if wallet.imported_keys:
- msg2 += "[b][color=#ff0000ff]" + _("WARNING") + "[/color]:[/b] " +\
- _("Your wallet contains imported keys. These keys cannot" +\
- " be recovered from seed.")
-
- def on_ok_press(_dlg, _btn):
- _dlg.close()
- if _btn != _dlg.ids.confirm:
- if not instance:
- self.change_password_dialog(wallet)
- return
- # confirm
- if instance is None:
- # in initial phase
- def create(password):
- try:
- password = None if not password else password
- wallet.save_seed(password)
- except Exception as err:
- Logger.Info('Wallet: {}'.format(err))
- Clock.schedule_once(lambda dt:
- app.show_error(err))
- wallet.synchronize() # generate first addresses offline
- self.waiting_dialog(
- partial(create, password),
- on_complete=partial(self.load_network, wallet, mode=mode))
-
- from electrum_gui.kivy.dialog import InitSeedDialog
- InitSeedDialog(message=msg2,
- seed_msg=brainwallet, seed=seed, on_release=on_ok_press).open()
-
- def change_password_dialog(self, wallet=None, instance=None, mode='create'):
- """Can be called directly (instance is None)
- or from a callback (instance is not None)"""
-
- if instance and not wallet.seed:
- return ShowError(_('No seed !!'), exit=True, modal=True)
-
- if instance is not None:
- if wallet.use_encryption:
- msg = (
- _('Your wallet is encrypted. Use this dialog to change" + \
- " your password.') + '\n' + _('To disable wallet" + \
- " encryption, enter an empty new password.'))
- mode = 'confirm'
- else:
- msg = _('Your wallet keys are not encrypted')
- mode = 'new'
- else:
- msg = _("Please choose a password to encrypt your wallet keys.") +\
- '\n' + _("Leave these fields empty if you want to disable" + \
- " encryption.")
-
- def on_release(_dlg, _btn):
- ti_password = _dlg.ids.ti_password
- ti_new_password = _dlg.ids.ti_new_password
- ti_confirm_password = _dlg.ids.ti_confirm_password
- if _btn != _dlg.ids.next:
- if mode == 'restore':
- # back is disabled cause seed is already set
- return
- _dlg.close()
- if not instance:
- # back on create
- CreateRestoreDialog(
- on_release=self.on_creatrestore_complete).open()
- return
-
- # Confirm
- wallet_name = _dlg.ids.ti_wallet_name.text
- password = (unicode(ti_password.text)
- if wallet.use_encryption else
- None)
- new_password = unicode(ti_new_password.text)
- new_password2 = unicode(ti_confirm_password.text)
-
- if new_password != new_password2:
- ti_password.text = ""
- ti_new_password.text = ""
- ti_confirm_password.text = ""
- if ti_password.disabled:
- ti_new_password.focus = True
- else:
- ti_password.focus = True
- return app.show_error(_('Passwords do not match'), duration=.5)
-
- if mode == 'restore':
- 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
- _dlg.close()
- #self.load_network(wallet, mode='create')
- return self.init_seed_dialog(password=new_password,
- wallet=wallet, wallet_name=wallet_name, mode=mode)
-
- try:
- seed = wallet.decode_seed(password)
- except BaseException:
- return app.show_error(_('Incorrect Password'), duration=.5)
-
- # test carefully
- try:
- wallet.update_password(seed, password, new_password)
- except BaseException:
- return app.show_error(_('Failed to update password'), exit=True)
- else:
- app.show_info_bubble(
- text=_('Password successfully updated'), duration=1,
- pos=_btn.pos)
- _dlg.close()
-
-
- if instance is None: # in initial phase
- self.load_wallet()
- self.app.update_wallet()
-
- from electrum_gui.kivy.dialog import ChangePasswordDialog
- cpd = ChangePasswordDialog(
- message=msg,
- mode=mode,
- on_release=on_release).open()
-
- def load_network(self, wallet, mode='create'):
- #if not self.config.get('server'):
- if self.network:
- if self.network.interfaces:
- if mode not in ('restore', 'create'):
- self.network_dialog()
- else:
- app.show_error(_('You are offline'))
- self.network.stop()
- self.network = None
-
- if mode in ('restore', 'create'):
- # auto cycle
- self.config.set_key('auto_cycle', True, True)
-
- # start wallet threads
- wallet.start_threads(self.network)
-
- if not mode == 'restore':
- return self.dispatch('on_wizard_complete', wallet)
-
- def get_text(text):
- def set_text(*l): app.info_bubble.ids.lbl.text=text
- Clock.schedule_once(set_text)
-
- def on_complete(*l):
- if not self.network:
- app.show_info(
- _("This wallet was restored offline. It may contain more"
- " addresses than displayed."), duration=.5)
- return self.dispatch('on_wizard_complete', wallet)
-
- if wallet.is_found():
- app.show_info(_("Recovery successful"), duration=.5)
- else:
- app.show_info(_("No transactions found for this seed"),
- duration=.5)
- return self.dispatch('on_wizard_complete', wallet)
-
- self.waiting_dialog(lambda: wallet.restore(get_text),
- on_complete=on_complete)
-
- def on_wizard_complete(self, wallet):
- pass
DIR diff --git a/gui/kivy/main.kv b/gui/kivy/main.kv
t@@ -1,7 +1,4 @@
#:import Window kivy.core.window.Window
-#:import _ electrum.i18n._
-#:import partial functools.partial
-
# Custom Global Widgets
t@@ -22,37 +19,36 @@
if root.state == 'normal' else 'icon_border')
size: root.size
pos: root.pos
-###########################
-## Gloabal Defaults
-###########################
-
-<Label>
- markup: True
- font_name: 'Roboto'
- font_size: '16sp'
-<ListItemButton>
- font_size: '12sp'
-
-#########################
-# Dialogs
-#########################
-
-################################################
-## Create Dialogs
-################################################
+<Butt_star@ActionToggleButton>:
+ important: True
+ size_hint_x: None
+ width: '32dp'
+ mipmap: True
+ state: 'down' if app.expert_mode else 'normal'
+ background_down: self.background_normal
+ foreground_color: (.466, .466, .466, 1)
+ color_active: (0.235, .588, .89, 1)
+ on_release: app.expert_mode = True if self.state == 'down' else False
+ Image:
+ source: 'atlas://gui/kivy/theming/light/star_big_inactive'
+ center: root.center
+ size: root.width/1.5, self.width
+ color:
+ root.foreground_color if root.state == 'normal' else root.color_active
+ canvas.after:
+ Color:
+ rgba: 1, 1, 1, 1
+ source:
+ allow_stretch: True
+
+<ELTextInput>
+ padding: '10dp', '4dp'
+ background_color: (0.238, 0.589, .996, 1) if self.focus else self.foreground_color
+ foreground_color: 0.531, 0.531, 0.531, 1
+ background_active: 'atlas://gui/kivy/theming/light/textinput_active'
+ background_normal: 'atlas://gui/kivy/theming/light/textinput_active'
-<CreateAccountTextInput@TextInput>
- border: 4, 4, 4, 4
- font_size: '15sp'
- padding: '15dp', '15dp'
- background_color: (1, 1, 1, 1) if self.focus else (0.454, 0.698, 0.909, 1)
- foreground_color: (0.31, 0.31, 0.31, 1) if self.focus else (0.835, 0.909, 0.972, 1)
- hint_text_color: self.foreground_color
- background_active: 'atlas://gui/kivy/theming/light/create_act_text_active'
- background_normal: 'atlas://gui/kivy/theming/light/create_act_text_active'
- size_hint_y: None
- height: '48sp'
<CreateAccountButtonBlue@Button>
canvas.after:
t@@ -75,26 +71,40 @@
text_size: self.size
halign: 'center'
valign: 'middle'
+ root: None
background_normal: 'atlas://gui/kivy/theming/light/btn_create_account'
background_down: 'atlas://gui/kivy/theming/light/btn_create_account'
background_disabled_normal: 'atlas://gui/kivy/theming/light/btn_create_act_disabled'
- on_release: self.root.dispatch('on_press', self)
- on_release: self.root.dispatch('on_release', self)
+ on_press: if self.root: self.root.dispatch('on_press', self)
+ on_release: if self.root: self.root.dispatch('on_release', self)
+
<CreateAccountButtonGreen@CreateAccountButtonBlue>
background_color: (1, 1, 1, 1) if self.disabled else (.415, .717, 0, 1 if self.state == 'normal' else .75)
+###########################
+## Gloabal Defaults
+###########################
+<TextInput>
+ on_focus: app._focused_widget = root
+
+<Label>
+ markup: True
+ font_name: 'Roboto'
+ font_size: '16sp'
+
+<ListItemButton>
+ font_size: '12sp'
+
+#########################
+# Dialogs
+#########################
<InfoBubble>
- canvas.before:
- Color:
- rgba: 0, 0, 0, .7 if root.dim_background else 0
- Rectangle:
- size: Window.size
size_hint: None, None
width: '270dp' if root.fs else min(self.width, dp(270))
height: self.width if self.fs else (lbl.texture_size[1] + dp(27))
BoxLayout:
- padding: '5dp'
+ padding: '5dp' if root.fs else 0
Widget:
size_hint: None, 1
width: '4dp' if root.fs else '2dp'
t@@ -117,346 +127,11 @@
size_hint: 1, 1
width: 0 if root.fs else (root.width - img.width)
-<-CreateAccountDialog>
- text_color: .854, .925, .984, 1
- auto_dismiss: False
- size_hint: None, None
- canvas.before:
- Color:
- rgba: 0, 0, 0, .9
- Rectangle:
- size: Window.size
- Color:
- rgba: .239, .588, .882, 1
- Rectangle:
- size: Window.size
-
- crcontent: crcontent
- # add electrum icon
- FloatLayout:
- size_hint: None, None
- size: 0, 0
- IconButton:
- id: but_close
- size_hint: None, None
- size: '27dp', '27dp'
- top: Window.height - dp(10)
- right: Window.width - dp(10)
- source: 'atlas://gui/kivy/theming/light/closebutton'
- on_release: root.dispatch('on_press', self)
- on_release: root.dispatch('on_release', self)
- BoxLayout:
- orientation: 'vertical' if self.width < self.height else 'horizontal'
- padding:
- min(dp(42), self.width/8), min(dp(60), self.height/9.7),\
- min(dp(42), self.width/8), min(dp(72), self.height/8)
- spacing: '27dp'
- GridLayout:
- id: grid_logo
- cols: 1
- pos_hint: {'center_y': .5}
- size_hint: 1, .62
- #height: self.minimum_height
- Image:
- id: logo_img
- mipmap: True
- allow_stretch: True
- size_hint: 1, None
- height: '110dp'
- source: 'atlas://gui/kivy/theming/light/electrum_icon640'
- Widget:
- size_hint: 1, None
- height: 0 if stepper.opacity else dp(15)
- Label:
- color: root.text_color
- opacity: 0 if stepper.opacity else 1
- text: 'ELECTRUM'
- size_hint: 1, None
- height: self.texture_size[1] if self.opacity else 0
- font_size: '33sp'
- font_name: 'data/fonts/tron/Tr2n.ttf'
- Image:
- id: stepper
- allow_stretch: True
- opacity: 0
- source: 'atlas://gui/kivy/theming/light/stepper_left'
- size_hint: 1, None
- height: grid_logo.height/2.5 if self.opacity else 0
- Widget:
- size_hint: None, None
- size: '5dp', '5dp'
- GridLayout:
- cols: 1
- id: crcontent
- spacing: '13dp'
-
-<CreateRestoreDialog>
- Label:
- color: root.text_color
- size_hint: 1, None
- text_size: self.width, None
- height: self.texture_size[1]
- text:
- _("Wallet file not found!!")+\
- "\n\n" + _("Do you want to create a new wallet ")+\
- _("or restore an existing one?")
- Widget
- size_hint: 1, None
- height: dp(15)
- GridLayout:
- id: grid
- orientation: 'vertical'
- cols: 1
- spacing: '14dp'
- size_hint: 1, None
- height: self.minimum_height
- CreateAccountButtonGreen:
- id: create
- text: _('Create a Wallet')
- root: root
- CreateAccountButtonBlue:
- id: restore
- text: _('I already have a wallet')
- root: root
- #CreateAccountButtonBlue:
- # id: watching
- # text: _('Create a Watching only wallet')
- # root: root
-
-<RestoreSeedDialog>
- GridLayout
- # leave room for future selection of gap through a widget
- # removed for mobile
- id: text_input_gap
- text: '5'
-
- cols: 1
- padding: 0, '12dp'
- orientation: 'vertical'
- spacing: '12dp'
- size_hint: 1, None
- height: self.minimum_height
- CreateAccountTextInput:
- id: text_input_seed
- size_hint: 1, None
- height: '110dp'
- hint_text:
- _('Enter your seedphrase')
- Label:
- font_size: '12sp'
- text_size: self.width, None
- size_hint: 1, None
- height: self.texture_size[1]
- halign: 'justify'
- valign: 'middle'
- text:
- _('If you need additional information, please check '
- '[color=#0000ff][ref=1]'
- 'https://electrum.org/faq.html#seed[/ref][/color]')
- on_ref_press:
- import webbrowser
- webbrowser.open('https://electrum.org/faq.html#seed')
- GridLayout:
- rows: 1
- spacing: '12dp'
- size_hint: 1, None
- height: self.minimum_height
- CreateAccountButtonBlue:
- id: back
- text: _('Back')
- root: root
- CreateAccountButtonGreen:
- id: next
- text: _('Next')
- root: root
-
-<InitSeedDialog>
- spacing: '12dp'
- GridLayout:
- id: grid
- cols: 1
- pos_hint: {'center_y': .5}
- size_hint_y: None
- height: dp(180)
- orientation: 'vertical'
- Button:
- border: 4, 4, 4, 4
- halign: 'justify'
- valign: 'middle'
- font_size: self.width/21
- text_size: self.width - dp(24), self.height - dp(12)
- #size_hint: 1, None
- #height: self.texture_size[1] + dp(24)
- background_normal: 'atlas://gui/kivy/theming/light/white_bg_round_top'
- background_down: self.background_normal
- text: root.message
- GridLayout:
- rows: 1
- size_hint: 1, .7
- #size_hint_y: None
- #height: but_seed.texture_size[1] + dp(24)
- Button:
- id: but_seed
- border: 4, 4, 4, 4
- halign: 'justify'
- valign: 'middle'
- font_size: self.width/15
- text: root.seed_msg
- text_size: self.width - dp(24), self.height - dp(12)
- background_normal: 'atlas://gui/kivy/theming/light/lightblue_bg_round_lb'
- background_down: self.background_normal
- Button:
- id: bt
- size_hint_x: .25
- background_normal: 'atlas://gui/kivy/theming/light/blue_bg_round_rb'
- background_down: self.background_normal
- Image:
- mipmap: True
- source: 'atlas://gui/kivy/theming/light/qrcode'
- size: bt.size
- center: bt.center
- #on_release:
- GridLayout:
- rows: 1
- spacing: '12dp'
- size_hint: 1, None
- height: self.minimum_height
- CreateAccountButtonBlue:
- id: back
- text: _('Back')
- root: root
- CreateAccountButtonGreen:
- id: confirm
- text: _('Confirm')
- root: root
-
-<ChangePasswordDialog>
- padding: '7dp'
- GridLayout:
- size_hint_y: None
- height: self.minimum_height
- cols: 1
- CreateAccountTextInput:
- id: ti_wallet_name
- hint_text: 'Your Wallet Name'
- multiline: False
- on_text_validate:
- next = ti_new_password if ti_password.disabled else ti_password
- next.focus = True
- Widget:
- size_hint_y: None
- height: '13dp'
- CreateAccountTextInput:
- id: ti_password
- hint_text: 'Enter old pincode'
- size_hint_y: None
- height: 0 if self.disabled else '38sp'
- password: True
- disabled: True if root.mode in ('new', 'create', 'restore') else False
- opacity: 0 if self.disabled else 1
- multiline: False
- on_text_validate:
- #root.validate_old_password()
- ti_new_password.focus = True
- Widget:
- size_hint_y: None
- height: 0 if ti_password.disabled else '13dp'
- CreateAccountTextInput:
- id: ti_new_password
- hint_text: 'Enter new pincode'
- multiline: False
- password: True
- on_text_validate: ti_confirm_password.focus = True
- Widget:
- size_hint_y: None
- height: '13dp'
- CreateAccountTextInput:
- id: ti_confirm_password
- hint_text: 'Confirm pincode'
- password: True
- multiline: False
- on_text_validate: root.validate_new_password()
- Widget
- GridLayout:
- rows: 1
- spacing: '12dp'
- size_hint: 1, None
- height: self.minimum_height
- CreateAccountButtonBlue:
- id: back
- text: _('Back')
- root: root
- disabled: True if root.mode[0] == 'r' else self.disabled
- CreateAccountButtonGreen:
- id: next
- text: _('Confirm') if root.mode[0] == 'r' else _('Next')
- root: root
-
-###############################################
-## Wallet Management
-###############################################
-
-<WalletManagement@ScrollView>
+StencilView:
+ manager: None
canvas.before:
Color:
- rgba: .145, .145, .145, 1
- Rectangle:
- size: root.size
- pos: root.pos
- VGridLayout:
- Wallets:
- id: wallets_section
- Plugins:
- id: plugins_section
- Commands:
- id: commands_section
-
-<WalletManagementItem@BoxLayout>
-
-<Header@WalletManagementItem>
-
-<Wallets@VGridLayout>
- Header
-
-<Plugins@VGridLayout>
- Header
-
-<Commands@VGridLayout>
- Header
-
-################################################
-## This is our Root Widget of the app
-################################################
-StencilView
- manager: manager
- Drawer
- id: drawer
- size: root.size
- WalletManagement
- id: wallet_management
- canvas.before:
- Color:
- rgba: .176, .176, .176, 1
- Rectangle:
- size: self.size
- pos: self.pos
- width:
- (root.width * .877) if app.ui_mode[0] == 'p'\
- else root.width * .35 if app.orientation[0] == 'l'\
- else root.width * .10
- height: root.height
- BoxLayout:
- x: wallet_management.width if app.ui_mode[0] == 't' else 0
- width: (root.width - self.x) if app.ui_mode[0] == 't' else root.width
- size_hint: None, None
- height: root.height
- canvas.before:
- Color
- rgba: 1, 1, 1, 1
- BorderImage
- border: 0, 32, 0, 0
- source: 'atlas://gui/kivy/theming/light/shadow_right'
- pos: root.pos
- size: self.x, self.height
- ScreenManager:
- id: manager
-\ No newline at end of file
+ rgba: 1, 1, 1, 1
+ Rectangle
+ size: self.size
+ pos: self.pos
+\ No newline at end of file
DIR diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py
t@@ -1,35 +1,88 @@
import sys
-from decimal import Decimal
+import datetime
from electrum import WalletStorage, Wallet
from electrum.i18n import _, set_language
-from electrum.wallet import format_satoshis
from kivy.app import App
from kivy.core.window import Window
-from kivy.lang import Builder
from kivy.logger import Logger
-from kivy.metrics import inch
from kivy.utils import platform
from kivy.properties import (OptionProperty, AliasProperty, ObjectProperty,
- StringProperty, ListProperty)
+ StringProperty, ListProperty, BooleanProperty)
+from kivy.cache import Cache
from kivy.clock import Clock
+from kivy.factory import Factory
-#inclusions for factory so that widgets can be used in kv
-from electrum_gui.kivy.drawer import Drawer
-from electrum_gui.kivy.dialog import InfoBubble
+from electrum_gui.kivy.uix.drawer import Drawer
-# delayed imports
-notification = None
+# lazy imports for factory so that widgets can be used in kv
+Factory.register('InstallWizard',
+ module='electrum_gui.kivy.uix.dialogs.installwizard')
+Factory.register('InfoBubble', module='electrum_gui.kivy.uix.dialogs')
+Factory.register('ELTextInput', module='electrum_gui.kivy.uix.screens')
+
+# delayed imports: for startup speed on android
+notification = app = Decimal = ref = format_satoshis = is_valid = Builder = None
+inch = None
+util = False
+re = None
+
+# register widget cache for keeping memory down timeout to 4 minutes to cache
+# the data
+Cache.register('electrum_widgets', timeout=240)
class ElectrumWindow(App):
- title = _('Electrum App')
+ def _get_bu(self):
+ assert self.decimal_point in (5,8)
+ return "BTC" if self.decimal_point == 8 else "mBTC"
- wallet = ObjectProperty(None)
- '''Holds the electrum wallet
+ def _set_bu(self, value):
+ try:
+ self.electrum_config.set_key('base_unit', value, True)
+ except AttributeError:
+ Logger.error('Electrum: Config not set '
+ 'While trying to save value to config')
- :attr:`wallet` is a `ObjectProperty` defaults to None.
+ base_unit = AliasProperty(_get_bu, _set_bu, bind=('decimal_point',))
+ '''BTC or UBTC or mBTC...
+
+ :attr:`base_unit` is a `AliasProperty` defaults to the unit set in
+ electrum config.
+ '''
+
+ currencies = ListProperty(['EUR', 'GBP', 'USD'])
+ '''List of currencies supported by the current exchanger plugin.
+
+ :attr:`currencies` is a `ListProperty` default to ['Eur', 'GBP'. 'USD'].
+ '''
+
+ expert_mode = BooleanProperty(False)
+ '''This defines whether expert mode options are available in the ui.
+
+ :attr:`expert_mode` is a `BooleanProperty` defaults to `False`.
+ '''
+
+ def _get_decimal(self):
+ try:
+ return self.electrum_config.get('decimal_point', 8)
+ except AttributeError:
+ return 8
+
+ def _set_decimal(self, value):
+ try:
+ self.electrum_config.set_key('decimal_point', value, True)
+ except AttributeError:
+ Logger.error('Electrum: Config not set '
+ 'While trying to save value to config')
+
+ decimal_point = AliasProperty(_get_decimal, _set_decimal)
+ '''This defines the decimal point to be used determining the
+ :attr:`decimal_point`.
+
+ :attr:`decimal_point` is a `AliasProperty` defaults to the value gotten
+ from electrum config.
'''
electrum_config = ObjectProperty(None)
t@@ -61,43 +114,26 @@ class ElectrumWindow(App):
'''Number of zeros used while representing the value in base_unit.
'''
- def _get_decimal(self):
- try:
- return self.electrum_config.get('decimal_point', 8)
- except AttributeError:
- return 8
-
- def _set_decimal(self, value):
- try:
- self.electrum_config.set_key('decimal_point', value, True)
- except AttributeError:
- Logger.error('Electrum: Config not set '
- 'While trying to save value to config')
-
- decimal_point = AliasProperty(_get_decimal, _set_decimal)
- '''This defines the decimal point to be used determining the
- :attr:`base_unit`.
+ navigation_higherarchy = ListProperty([])
+ '''This is a list of the current navigation higherarchy of the app used to
+ navigate using back button.
- :attr:`decimal_point` is a `AliasProperty` defaults to the value gotten
- from electrum config.
+ :attr:`navigation_higherarchy` is s `ListProperty` defaults to []
'''
- def _get_bu(self):
- assert self.decimal_point in (5,8)
- return "BTC" if self.decimal_point == 8 else "mBTC"
+ _orientation = OptionProperty('landscape',
+ options=('landscape', 'portrait'))
- def _set_bu(self, value):
- try:
- self.electrum_config.set_key('base_unit', value, True)
- except AttributeError:
- Logger.error('Electrum: Config not set '
- 'While trying to save value to config')
+ def _get_orientation(self):
+ return self._orientation
- base_unit = AliasProperty(_get_bu, _set_bu, bind=('decimal_point',))
- '''BTC or UBTC or mBTC...
+ orientation = AliasProperty(_get_orientation,
+ None,
+ bind=('_orientation',))
+ '''Tries to ascertain the kind of device the app is running on.
+ Cane be one of `tablet` or `phone`.
- :attr:`base_unit` is a `AliasProperty` defaults to the unit set in
- electrum config.
+ :data:`orientation` is a read only `AliasProperty` Defaults to 'landscape'
'''
_ui_mode = OptionProperty('phone', options=('tablet', 'phone'))
t@@ -114,51 +150,58 @@ class ElectrumWindow(App):
:data:`ui_mode` is a read only `AliasProperty` Defaults to 'phone'
'''
- _orientation = OptionProperty('landscape',
- options=('landscape', 'portrait'))
-
- def _get_orientation(self):
- return self._orientation
-
- orientation = AliasProperty(_get_orientation,
- None,
- bind=('_orientation',))
- '''Tries to ascertain the kind of device the app is running on.
- Cane be one of `tablet` or `phone`.
-
- :data:`orientation` is a read only `AliasProperty` Defaults to 'landscape'
+ url = StringProperty('', allownone=True)
+ '''
'''
- navigation_higherarchy = ListProperty([])
- '''This is a list of the current navigation higherarchy of the app used to
- navigate using back button.
+ wallet = ObjectProperty(None)
+ '''Holds the electrum wallet
- :attr:`navigation_higherarchy` is s `ListProperty` defaults to []
+ :attr:`wallet` is a `ObjectProperty` defaults to None.
'''
__events__ = ('on_back', )
def __init__(self, **kwargs):
# initialize variables
- self.info_bubble = None
+ self._clipboard = None
self.console = None
self.exchanger = None
+ self.info_bubble = None
+ self.qrscanner = None
+ self.nfcscanner = None
+ self.tabs = None
super(ElectrumWindow, self).__init__(**kwargs)
- self.network = network = kwargs.get('network')
- self.electrum_config = config = kwargs.get('config')
+ title = _('Electrum App')
+ self.network = network = kwargs.get('network', None)
+ self.electrum_config = config = kwargs.get('config', None)
+ self.gui_object = kwargs.get('gui_object', None)
+
+ self.bind(url=self.set_url)
+ # were we sent a url?
+ url = kwargs.get('url', None)
+ if url:
+ self.gui_object.set_url(url)
# create triggers so as to minimize updation a max of 2 times a sec
+ self._trigger_update_wallet =\
+ Clock.create_trigger(self.update_wallet, .5)
self._trigger_update_status =\
Clock.create_trigger(self.update_status, .5)
self._trigger_update_console =\
Clock.create_trigger(self.update_console, .5)
self._trigger_notify_transactions = \
- Clock.create_trigger(self.notify_transactions, .5)
+ Clock.create_trigger(self.notify_transactions, 5)
+
+ def set_url(self, instance, url):
+ self.gui_object.set_url(url)
def build(self):
- from kivy.lang import Builder
+ global Builder
+ if not Builder:
+ from kivy.lang import Builder
return Builder.load_file('gui/kivy/main.kv')
def _pause(self):
t@@ -172,11 +215,13 @@ class ElectrumWindow(App):
def on_start(self):
''' This is the start point of the kivy ui
'''
- Window.bind(size=self.on_size,
+ win = Window
+ win.bind(size=self.on_size,
on_keyboard=self.on_keyboard)
- Window.bind(on_key_down=self.on_key_down)
+ win.bind(on_key_down=self.on_key_down)
- # register fonts
+ # Register fonts without this you won't be able to use bold/italic...
+ # inside markup.
from kivy.core.text import Label
Label.register('Roboto',
'data/fonts/Roboto.ttf',
t@@ -185,23 +230,29 @@ class ElectrumWindow(App):
'data/fonts/Roboto-Bold.ttf')
if platform == 'android':
- #
- Window.bind(keyboard_height=self.on_keyboard_height)
- self.on_size(Window, Window.size)
+ # bind to keyboard height so we can get the window contents to
+ # behave the way we want when the keyboard appears.
+ win.bind(keyboard_height=self.on_keyboard_height)
+
+ self.on_size(win, win.size)
config = self.electrum_config
storage = WalletStorage(config)
Logger.info('Electrum: Check for existing wallet')
- if not storage.file_exists:
+
+ if storage.file_exists:
+ wallet = Wallet(storage)
+ action = wallet.get_action()
+ else:
+ action = 'new'
+
+ if action is not None:
# start installation wizard
Logger.debug('Electrum: Wallet not found. Launching install wizard')
- import installwizard
- wizard = installwizard.InstallWizard(config, self.network,
- storage)
+ wizard = Factory.InstallWizard(config, self.network, storage)
wizard.bind(on_wizard_complete=self.on_wizard_complete)
- wizard.run()
+ wizard.run(action)
else:
- wallet = Wallet(storage)
wallet.start_threads(self.network)
self.on_wizard_complete(None, wallet)
t@@ -220,15 +271,22 @@ class ElectrumWindow(App):
# capture back button and pause app.
self._pause()
- def on_keyboard_height(self, *l):
- from kivy.animation import Animation
- from kivy.uix.popup import Popup
- active_widg = Window.children[0]
- active_widg = active_widg\
- if (active_widg == self.root or\
- issubclass(active_widg.__class__, Popup)) else\
- Window.children[1]
- Animation(y=Window.keyboard_height, d=.1).start(active_widg)
+ def on_keyboard_height(self, window, height):
+ win = window
+ active_widg = win.children[0]
+ if not issubclass(active_widg.__class__, Factory.Popup):
+ try:
+ active_widg = self.root.children[0]
+ except IndexError:
+ return
+
+ try:
+ fw = self._focused_widget
+ except AttributeError:
+ return
+ if height > 0 and fw.to_window(*fw.pos)[1] > height:
+ return
+ Factory.Animation(y=win.keyboard_height, d=.1).start(active_widg)
def on_key_down(self, instance, key, keycode, codepoint, modifiers):
if 'ctrl' in modifiers:
t@@ -261,6 +319,7 @@ class ElectrumWindow(App):
def on_wizard_complete(self, instance, wallet):
if not wallet:
Logger.debug('Electrum: No Wallet set/found. Exiting...')
+ app = App.get_running_app()
app.show_error('Electrum: No Wallet set/found. Exiting...',
exit=True)
t@@ -271,21 +330,6 @@ class ElectrumWindow(App):
self.load_wallet(wallet)
- #TODO: URI handling
- #self.windows.append(w)
- #if url: w.set_url(url)
-
- # 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
tasks of setting up the ui.
t@@ -297,49 +341,70 @@ class ElectrumWindow(App):
#self._tray_icon = 'icons/" + (electrum_dark_icon.png'\
# if platform == 'mac' else 'electrum_light_icon.png')
- #setup tray
+ #setup tray TODO: use the systray branch
#self.tray = SystemTrayIcon(self.icon, self)
#self.tray.setToolTip('Electrum')
#self.tray.activated.connect(self.tray_activated)
+ global ref
+ if not ref:
+ from weakref import ref
+
set_language(self.electrum_config.get('language'))
self.funds_error = False
self.completions = []
# setup UX
- self.screens = ['mainscreen']
- self.load_screen(index=0)
-
- self.icon = "icons/electrum.png"
+ self.screens = ['mainscreen',]
+
+ #setup lazy imports for mainscreen
+ Factory.register('AnimatedPopup',
+ module='electrum_gui.kivy.uix.dialogs')
+ Factory.register('TabbedCarousel',
+ module='electrum_gui.kivy.uix.screens')
+ Factory.register('ScreenDashboard',
+ module='electrum_gui.kivy.uix.screens')
+ Factory.register('EffectWidget',
+ module='electrum_gui.kivy.uix.effectwidget')
# load and focus the ui
+ #Load mainscreen
+
+ Factory.register('QRCodeWidget',
+ module='electrum_gui.kivy.uix.qrcodewidget')
+ Factory.register('MainScreen',
+ module='electrum_gui.kivy.uix.screens')
+ Factory.register('CSpinner',
+ module='electrum_gui.kivy.uix.screens')
+
+ dr = Builder.load_file('gui/kivy/uix/ui_screens/mainscreen.kv')
+ self.root.add_widget(dr)
+ self.root.manager = manager = dr.ids.manager
+ self.root.main_screen = m = manager.screens[0]
+ self.tabs = m.ids.tabs
+
+ #TODO
+ # load left_menu
+
+ self.icon = "icons/electrum.png"
# connect callbacks
if self.network:
- self.network.register_callback(
- 'updated', self._trigger_update_status)
- self.network.register_callback(
- 'banner', self._trigger_update_console)
- self.network.register_callback(
- 'disconnected', self._trigger_update_status)
- self.network.register_callback(
- 'disconnecting', self._trigger_update_status)
- self.network.register_callback('new_transaction',
- self._trigger_notify_transactions)
+ self.network.register_callback('updated', self._trigger_update_wallet)
+ #self.network.register_callback('banner', self.console.show_message(self.network.banner))
+ self.network.register_callback('disconnected', self._trigger_update_status)
+ self.network.register_callback('disconnecting', self._trigger_update_status)
+ self.network.register_callback('new_transaction', self._trigger_notify_transactions)
# set initial message
- self.update_console()
+ #self.console.show_message(self.network.banner)
self.wallet = None
def create_quote_text(self, btc_balance, mode='normal'):
'''
'''
- if not self.exchanger:
- from electrum_gui.kivy.plugins.exchange_rate import Exchanger
- self.exchanger = Exchanger(self)
- self.exchanger.start()
quote_currency = self.exchanger.currency
quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
t@@ -348,19 +413,60 @@ class ElectrumWindow(App):
quote_currency)
if quote_balance is None:
- quote_text = ""
+ quote_text = u"..."
else:
- quote_text = " (%.2f %s)" % (quote_balance, quote_currency)
+ quote_text = u"%s%.2f" % (quote_currency,
+ quote_balance)
return quote_text
def set_currencies(self, quote_currencies):
- self._trigger_update_status()
- print quote_currencies
self.currencies = sorted(quote_currencies.keys())
+ self._trigger_update_status()
+
+ def get_history_rate(self, item, btc_balance, mintime):
+ '''Historical rates: currently only using coindesk by default.
+ '''
+ maxtime = datetime.datetime.now().strftime('%Y-%m-%d')
+ rate = self.exchanger.get_history_rate(item, btc_balance, mintime,
+ maxtime)
+
+ return self.set_history_rate(item, rate)
+
+ def set_history_rate(self, item, rate):
+ '''
+ '''
+ #TODO: fix me allow other currencies to be used for history rates
+ quote_currency = self.exchanger.symbols.get('USD', 'USD')
+
+ if rate is None:
+ quote_text = "..."
+ else:
+ quote_text = "{0}{1:.3}".format(quote_currency, rate)
+
+ item = item()
+ if item:
+ item.quote_text = quote_text
+ return quote_text
def update_console(self, *dt):
- if self.console:
- self.console.showMessage(self.network.banner)
+ console = self.console
+ if console:
+ console = self.console
+ console.history = self.config.get("console-history",[])
+ console.history_index = len(console.history)
+
+ console.updateNamespace({'wallet' : self.wallet, 'network' : self.network, 'gui':self})
+ console.updateNamespace({'util' : util, 'bitcoin':bitcoin})
+
+ c = commands.Commands(self.wallet, self.network, lambda: self.console.set_json(True))
+ methods = {}
+ def mkfunc(f, method):
+ return lambda *args: apply( f, (method, args, self.password_dialog ))
+ for m in dir(c):
+ if m[0]=='_' or m in ['network','wallet']: continue
+ methods[m] = mkfunc(c._run, m)
+
+ console.updateNamespace(methods)
def load_wallet(self, wallet):
self.wallet = wallet
t@@ -375,25 +481,28 @@ class ElectrumWindow(App):
self.update_wallet()
# Once GUI has been initialized check if we want to announce something
# since the callback has been called before the GUI was initialized
+ self.update_history_tab()
self.notify_transactions()
self.update_account_selector()
- #TODO
- #self.new_account.setEnabled(self.wallet.seed_version>4)
- #self.update_lock_icon()
- #self.update_buttons_on_seed()
#run_hook('load_wallet', wallet)
def update_status(self, *dt):
if not self.wallet:
return
+
+ global Decimal
+ if not Decimal:
+ from decimal import Decimal
+
+ unconfirmed = ''
+ quote_text = ''
+
if self.network is None or not self.network.is_running():
text = _("Offline")
#icon = QIcon(":icons/status_disconnected.png")
elif self.network.is_connected():
- unconfirmed = ''
- quote_text = '.'
if not self.wallet.up_to_date:
text = _("Synchronizing...")
#icon = QIcon(":icons/status_waiting.png")
t@@ -406,7 +515,8 @@ class ElectrumWindow(App):
if u:
unconfirmed = " [%s unconfirmed]"\
%( self.format_amount(u, True).strip())
- quote_text = self.create_quote_text(Decimal(c+u)/100000000) or '.'
+ quote_text = self.create_quote_text(Decimal(c+u)/100000000,
+ mode='symbol') or ''
#r = {}
#run_hook('set_quote_text', c+u, r)
t@@ -420,29 +530,42 @@ class ElectrumWindow(App):
text = _("Not connected")
#icon = QIcon(":icons/status_disconnected.png")
- #TODO
- #status_card = self.root.main_screen.ids.tabs.ids.\
- # screen_dashboard.ids.status_card
+ try:
+ status_card = self.root.main_screen.ids.tabs.ids.\
+ screen_dashboard.ids.status_card
+ except AttributeError:
+ return
self.status = text.strip()
- #status_card.quote_text = quote_text.strip()
- #status_card.uncomfirmed = unconfirmed.strip()
- ##app.base_unit = self.base_unit().strip()
+ status_card.quote_text = quote_text.strip()
+ status_card.uncomfirmed = unconfirmed.strip()
def format_amount(self, x, is_diff=False, whitespaces=False):
'''
'''
- return format_satoshis(x, is_diff, self.num_zeros, self.decimal_point, whitespaces)
-
- def update_wallet(self):
+ global format_satoshis
+ if not format_satoshis:
+ from electrum.wallet import format_satoshis
+ return format_satoshis(x, is_diff, self.num_zeros,
+ self.decimal_point, whitespaces)
+
+ def read_amount(self, x):
+ if x in['.', '']:
+ return None
+ p = pow(10, self.decimal_point)
+ return int( p * Decimal(x) )
+
+ def update_wallet(self, *dt):
'''
'''
- self.update_status()
- if (self.wallet.up_to_date or
- not self.network or not self.network.is_connected()):
- #TODO
- #self.update_history_tab()
- #self.update_receive_tab()
- #self.update_contacts_tab()
+ if not self.exchanger:
+ from electrum_gui.kivy.plugins.exchange_rate import Exchanger
+ self.exchanger = Exchanger(self)
+ self.exchanger.start()
+ return
+ self._trigger_update_status()
+ if (self.wallet.up_to_date or not self.network or not self.network.is_connected()):
+ self.update_history_tab()
+ self.update_contacts_tab()
self.update_completions()
def update_account_selector(self):
t@@ -458,54 +581,54 @@ class ElectrumWindow(App):
else:
self.account_selector.hide()
- def update_history_tab(self, see_all=False):
- def parse_histories(items):
- results = []
- for item in items:
- tx_hash, conf, is_mine, value, fee, balance, timestamp = item
- if conf > 0:
- try:
- time_str = datetime.datetime.fromtimestamp(
- timestamp).isoformat(' ')[:-3]
- except:
- time_str = _("unknown")
-
- if conf == -1:
- time_str = _('unverified')
- icon = "atlas://gui/kivy/theming/light/close"
- elif conf == 0:
- time_str = _('pending')
- icon = "atlas://gui/kivy/theming/light/unconfirmed"
- elif conf < 6:
- time_str = '' # add new to fix error when conf < 0
- conf = max(1, conf)
- icon = "atlas://gui/kivy/theming/light/clock{}".format(conf)
- else:
- icon = "atlas://gui/kivy/theming/light/confirmed"
+ def parse_histories(self, items):
+ for item in items:
+ tx_hash, conf, is_mine, value, fee, balance, timestamp = item
+ time_str = _("unknown")
+ if conf > 0:
+ try:
+ time_str = datetime.datetime.fromtimestamp(
+ timestamp).isoformat(' ')[:-3]
+ except Exception:
+ time_str = _("error")
+
+ if conf == -1:
+ time_str = _('unverified')
+ icon = "atlas://gui/kivy/theming/light/close"
+ elif conf == 0:
+ time_str = _('pending')
+ icon = "atlas://gui/kivy/theming/light/unconfirmed"
+ elif conf < 6:
+ time_str = '' # add new to fix error when conf < 0
+ conf = max(1, conf)
+ icon = "atlas://gui/kivy/theming/light/clock{}".format(conf)
+ else:
+ icon = "atlas://gui/kivy/theming/light/confirmed"
- if value is not None:
- v_str = self.format_amount(value, True, whitespaces=True)
- else:
- v_str = '--'
+ if value is not None:
+ v_str = self.format_amount(value, True, whitespaces=True)
+ else:
+ v_str = '--'
- balance_str = self.format_amount(balance, whitespaces=True)
+ balance_str = self.format_amount(balance, whitespaces=True)
- if tx_hash:
- label, is_default_label = self.wallet.get_label(tx_hash)
- else:
- label = _('Pruned transaction outputs')
- is_default_label = False
+ if tx_hash:
+ label, is_default_label = self.wallet.get_label(tx_hash)
+ else:
+ label = _('Pruned transaction outputs')
+ is_default_label = False
- results.append((
- conf, icon, time_str, label, v_str, balance_str, tx_hash))
+ yield (conf, icon, time_str, label, v_str, balance_str, tx_hash)
- return results
+ def update_history_tab(self, see_all=False):
- history_card = self.root.main_screen.ids.tabs.ids.\
+ try:
+ history_card = self.root.main_screen.ids.tabs.ids.\
screen_dashboard.ids.recent_activity_card
- histories = parse_histories(reversed(
+ except AttributeError:
+ return
+ histories = self.parse_histories(reversed(
self.wallet.get_tx_history(self.current_account)))
- #history_view.content_adapter.data = histories
# repopulate History Card
last_widget = history_card.ids.content.children[-1]
t@@ -513,26 +636,34 @@ class ElectrumWindow(App):
history_add = history_card.ids.content.add_widget
history_add(last_widget)
RecentActivityItem = Factory.RecentActivityItem
-
- history_card.ids.btn_see_all.opacity = (0 if see_all or
- len(histories) < 8 else 1)
- if not see_all:
- histories = histories[:8]
-
- create_quote_text = self.create_quote_text
+ global Decimal, ref
+ if not ref:
+ from weakref import ref
+ if not Decimal:
+ from decimal import Decimal
+
+ get_history_rate = self.get_history_rate
+ count = 0
for items in histories:
+ count += 1
conf, icon, date_time, address, amount, balance, tx = items
ri = RecentActivityItem()
ri.icon = icon
ri.date = date_time
+ mintimestr = date_time.split()[0]
ri.address = address
- ri.amount = amount
- ri.quote_text = create_quote_text(
- Decimal(amount)/100000000, mode='symbol')
+ ri.amount = amount.strip()
+ ri.quote_text = get_history_rate(ref(ri),
+ Decimal(amount),
+ mintimestr)
ri.balance = balance
ri.confirmations = conf
ri.tx_hash = tx
history_add(ri)
+ if count == 8 and not see_all:
+ break
+
+ history_card.ids.btn_see_all.opacity = (0 if count < 8 else 1)
def update_receive_tab(self):
#TODO move to address managment
t@@ -590,24 +721,45 @@ class ElectrumWindow(App):
receive_list.content_adapter.data = data
def update_contacts_tab(self):
- data = []
+ contact_list = self.root.main_screen.ids.tabs.ids.\
+ screen_contacts.ids.contact_container
+ #contact_list.clear_widgets()
+
+ child = -1
+ children = contact_list.children
for address in self.wallet.addressbook:
label = self.wallet.labels.get(address, '')
- item = (address, label, "%d" % self.wallet.get_num_tx(address))
- data.append(item)
- # item.setFont(0, QFont(MONOSPACE_FONT))
- # # 32 = label can be edited (bool)
- # item.setData(0,32, True)
- # # 33 = payto string
- # item.setData(0,33, address)
+ child += 1
+ try:
+ if children[child].label == label:
+ continue
+ except IndexError:
+ pass
+ tx = self.wallet.get_num_tx(address)
+ ci = Factory.ContactItem()
+ ci.address = address
+ ci.label = label
+ ci.tx_amount = tx
+ contact_list.add_widget(ci)
+
+ #self.run_hook('update_contacts_tab')
+
+ def set_pay_from(self, l):
+ #TODO
+ return
+ self.pay_from = l
+ self.from_list.clear()
+ self.from_label.setHidden(len(self.pay_from) == 0)
+ self.from_list.setHidden(len(self.pay_from) == 0)
+ for addr in self.pay_from:
+ c, u = self.wallet.get_addr_balance(addr)
+ balance = self.format_amount(c + u)
+ self.from_list.addTopLevelItem(QTreeWidgetItem( [addr, balance] ))
- self.run_hook('update_contacts_tab')
- contact_list = app.root.main_screen.ids.tabs.ids.\
- screen_contacts.ids.contacts_list
- contact_list.content_adapter.data = data
def update_completions(self):
+ #TODO: check and remove if not used
l = []
for addr, label in self.wallet.labels.items():
if addr in self.wallet.addressbook:
t@@ -616,19 +768,134 @@ class ElectrumWindow(App):
#self.run_hook('update_completions', l)
self.completions = l
+ def protected(func):
+ return lambda s, *args, **kwargs: s.do_protect(func, args, **kwargs)
+
+ def do_protect(self, func, **kwargs):
+ print kwargs
+ instance = kwargs.get('instance', None)
+ password = kwargs.get('password', None)
+ message = kwargs.get('message', '')
+
+ def run_func(instance=None, password=None):
+ args = (self, instance, password)
+ apply(func, args)
+
+ if self.wallet.use_encryption:
+ return self.password_required_dialog(post_ok=run_func, message=message)
+
+ return run_func()
+
+ def do_send(self):
+ app = App.get_running_app()
+ screen_send = app.root.main_screen.ids.tabs.ids.screen_send
+ scrn = screen_send.content.ids
+ label = unicode(scrn.message_e.text)
+ # TODO
+ #if self.gui_object.payment_request:
+ # outputs = self.gui_object.payment_request.outputs
+ # amount = self.gui_object.payment_request.get_amount()
+ #else:
+
+ r = unicode(scrn.payto_e.text).strip()
+
+ # label or alias, with address in brackets
+ global re
+ if not re:
+ import re
+ m = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
+ to_address = m.group(2) if m else r
+
+ global is_valid
+ if not is_valid:
+ from electrum.bitcoin import is_valid
+
+ if not is_valid(to_address):
+ app.show_error(_('Invalid Bitcoin Address') +
+ ':\n' + to_address)
+ return
+
+ try:
+ amount = self.read_amount(unicode(scrn.amount_e.text))
+ except Exception:
+ app.show_error(_('Invalid Amount'))
+ return
+ try:
+ fee = self.read_amount(unicode(scrn.fee_e.amt))
+ except Exception as err:
+ print err
+ app.show_error(_('Invalid Fee'))
+ return
+
+ from pudb import set_trace; set_trace()
+ message = 'sending {} {} to {}'.format(\
+ app.base_unit, scrn.amount_e.text, r)
+
+ confirm_fee = self.config.get('confirm_fee', 100000)
+ if fee >= confirm_fee:
+ if not self.question(_("The fee for this transaction seems unusually high.\nAre you really sure you want to pay %(fee)s in fees?")%{ 'fee' : self.format_amount(fee) + ' '+ self.base_unit()}):
+ return
+
+ self.send_tx(to_address, amount, fee, label)
+
+ @protected
+ def send_tx(self, outputs, fee, label, password):
+
+ # first, create an unsigned tx
+ domain = self.get_payment_sources()
+ try:
+ tx = self.wallet.make_unsigned_transaction(outputs, fee, None, domain)
+ tx.error = None
+ except Exception as e:
+ traceback.print_exc(file=sys.stdout)
+ self.show_info(str(e))
+ return
+
+ # call hook to see if plugin needs gui interaction
+ #run_hook('send_tx', tx)
+
+ # sign the tx
+ def sign_thread():
+ time.sleep(0.1)
+ keypairs = {}
+ self.wallet.add_keypairs_from_wallet(tx, keypairs, password)
+ self.wallet.sign_transaction(tx, keypairs, password)
+ return tx, fee, label
+
+ def sign_done(tx, fee, label):
+ if tx.error:
+ self.show_info(tx.error)
+ return
+ if tx.requires_fee(self.wallet.verifier) and fee < MIN_RELAY_TX_FEE:
+ self.show_error(_("This transaction requires a higher fee, or "
+ "it will not be propagated by the network."))
+ return
+ if label:
+ self.wallet.set_label(tx.hash(), label)
+
+ if not self.gui_object.payment_request:
+ if not tx.is_complete() or self.config.get('show_before_broadcast'):
+ self.show_transaction(tx)
+ return
+
+ self.broadcast_transaction(tx)
+
+ WaitingDialog(self, 'Signing..').start(sign_thread, sign_done)
+
def notify_transactions(self, *dt):
'''
'''
if not self.network or not self.network.is_connected():
return
- iface = self.network.interface
- if len(iface.pending_transactions_for_notifications) > 0:
+ iface = self.network
+ ptfn = iface.pending_transactions_for_notifications
+ if len(ptfn) > 0:
# Combine the transactions if there are more then three
- tx_amount = len(iface.pending_transactions_for_notifications)
+ tx_amount = len(ptfn)
if(tx_amount >= 3):
total_amount = 0
- for tx in iface.pending_transactions_for_notifications:
+ for tx in ptfn:
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
if(v > 0):
total_amount += v
t@@ -645,11 +912,18 @@ class ElectrumWindow(App):
iface.pending_transactions_for_notifications.remove(tx)
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
if(v > 0):
- from pudb import set_trace; set_trace()
self.notify(
- _("New transaction received. {amount}s {unit}s").
+ _("{} new transaction received. {amount}s {unit}s").
format( amount=self.format_amount(v),
- unit=self.base_unit()))
+ unit=self.base_unit))
+
+ def copy(self, text):
+ ''' Copy provided text to clipboard
+ '''
+ if not self._clipboard:
+ from kivy.core.clipboard import Clipboard
+ self._clipboard = Clipboard
+ self._clipboard.put(text, 'text/plain')
def notify(self, message):
try:
t@@ -668,31 +942,42 @@ class ElectrumWindow(App):
'''
'''
# pause nfc
- # pause qrscanner(Camera) if active
+ if self.qrscanner:
+ self.qrscanner.stop()
+ if self.nfcscanner:
+ self.nfcscanner.nfc_disable()
return True
def on_resume(self):
'''
'''
- # resume nfc
- # resume camera if active
- pass
+ if self.qrscanner and qrscanner.get_parent_window():
+ self.qrscanner.start()
+ if self.nfcscanner:
+ self.nfcscanner.nfc_enable()
def on_size(self, instance, value):
width, height = value
self._orientation = 'landscape' if width > height else 'portrait'
+
+ global inch
+ if not inch:
+ from kivy.metrics import inch
+
self._ui_mode = 'tablet' if min(width, height) > inch(3.51) else 'phone'
Logger.debug('orientation: {} ui_mode: {}'.format(self._orientation,
self._ui_mode))
- def load_screen(self, index=0, direction='left', manager=None):
+ def load_screen(self, index=0, direction='left', manager=None, switch=True):
''' Load the appropriate screen as mentioned in the parameters.
'''
manager = manager or self.root.manager
- screen = Builder.load_file('gui/kivy/ui_screens/'\
+ screen = Builder.load_file('gui/kivy/uix/ui_screens/'\
+ self.screens[index] + '.kv')
screen.name = self.screens[index]
- manager.switch_to(screen, direction=direction)
+ if switch:
+ manager.switch_to(screen, direction=direction)
+ return screen
def load_next_screen(self):
'''
t@@ -705,7 +990,7 @@ class ElectrumWindow(App):
self.load_screen()
def load_previous_screen(self):
- '''
+ ''' Load the previous screen from disk.
'''
manager = root.manager
try:
t@@ -715,51 +1000,239 @@ class ElectrumWindow(App):
except IndexError:
pass
- def show_error(self, error,
- width='200dp',
- pos=None,
- arrow_pos=None,
- exit=False,
- icon='atlas://gui/kivy/theming/light/error',
- duration=0,
- modal=False):
+ def save_new_contact(self, address, label):
+ address = unicode(address)
+ label = unicode(label)
+ global is_valid
+ if not is_valid:
+ from electrum.bitcoin import is_valid
+
+
+ if is_valid(address):
+ if label:
+ self.set_label(address, text=label)
+ self.wallet.add_contact(address)
+ self.update_contacts_tab()
+ self.update_history_tab()
+ self.update_completions()
+ else:
+ self.show_error(_('Invalid Address'))
+
+ def send_payment(self, address, amount=0, label='', message=''):
+ tabs = self.tabs
+ screen_send = tabs.ids.screen_send
+
+ if label and self.wallet.labels.get(address) != label:
+ #if self.question('Give label "%s" to address %s ?'%(label,address)):
+ if address not in self.wallet.addressbook and not self.wallet. is_mine(address):
+ self.wallet.addressbook.append(address)
+ self.wallet.set_label(address, label)
+
+ # switch_to the send screen
+ tabs.ids.panel.switch_to(tabs.ids.tab_send)
+
+ label = self.wallet.labels.get(address)
+ m_addr = label + ' <'+ address +'>' if label else address
+
+ # populate
+ def set_address(*l):
+ content = screen_send.content.ids
+ content.payto_e.text = m_addr
+ content.message_e.text = message
+ if amount:
+ content.amount_e.text = amount
+
+ # wait for screen to load
+ Clock.schedule_once(set_address, .5)
+
+ def set_send(self, address, amount, label, message):
+ self.send_payment(address, amount=amount, label=label, message=message)
+
+ def prepare_for_payment_request(self):
+ tabs = self.tabs
+ screen_send = tabs.ids.screen_send
+
+ # switch_to the send screen
+ tabs.ids.panel.switch_to(tabs.ids.tab_send)
+
+ content = screen_send.content.ids
+ if content:
+ self.set_frozen(content, False)
+ screen_send.screen_label.text = _("please wait...")
+ return True
+
+ def payment_request_ok(self):
+ tabs = self.tabs
+ screen_send = tabs.ids.screen_send
+
+ # switch_to the send screen
+ tabs.ids.panel.switch_to(tabs.ids.tab_send)
+
+ content = screen_send.content
+ self.set_frozen(content, True)
+
+ content.ids.payto_e.text = self.gui_object.payment_request.domain
+ content.ids.amount_e.text = self.format_amount(self.gui_object.payment_request.get_amount())
+ content.ids.message_e.text = self.gui_object.payment_request.memo
+
+ # wait for screen to load
+ Clock.schedule_once(set_address, .5)
+
+ def do_clear(self):
+ tabs = self.tabs
+ screen_send = tabs.ids.screen_send
+ content = screen_send.ids.content
+ cts = content.ids
+ cts.payto_e.text = cts.message_e.text = cts.amount_e.text = \
+ cts.fee_e.text = ''
+
+ self.set_frozen(content, False)
+
+ self.set_pay_from([])
+ self.update_status()
+
+ def set_frozen(self, entry, frozen):
+ if frozen:
+ entry.disabled = True
+ Factory.Animation(opacity=0).start(content)
+ else:
+ entry.disabled = False
+ Factory.Animation(opacity=1).start(content)
+
+ def set_addrs_frozen(self,addrs,freeze):
+ for addr in addrs:
+ if not addr: continue
+ if addr in self.wallet.frozen_addresses and not freeze:
+ self.wallet.unfreeze(addr)
+ elif addr not in self.wallet.frozen_addresses and freeze:
+ self.wallet.freeze(addr)
+ self.update_receive_tab()
+
+ def payment_request_error(self):
+ tabs = self.tabs
+ screen_send = tabs.ids.screen_send
+
+ # switch_to the send screen
+ tabs.ids.panel.switch_to(tabs.ids.tab_send)
+
+ self.do_clear()
+ self.show_info(self.gui_object.payment_request.error)
+
+ def encode_uri(self, addr, amount=0, label='',
+ message='', size='', currency='btc'):
+ ''' Convert to BIP0021 compatible URI
+ '''
+ uri = 'bitcoin:{}'.format(addr)
+ first = True
+ if amount:
+ uri += '{}amount={}'.format('?' if first else '&', amount)
+ first = False
+ if label:
+ uri += '{}label={}'.format('?' if first else '&', label)
+ first = False
+ if message:
+ uri += '{}?message={}'.format('?' if first else '&', message)
+ first = False
+ if size:
+ uri += '{}size={}'.format('?' if not first else '&', size)
+ return uri
+
+ def decode_uri(self, uri):
+ if ':' not in uri:
+ # It's just an address (not BIP21)
+ return {'address': uri}
+
+ if '//' not in uri:
+ # Workaround for urlparse, it don't handle bitcoin: URI properly
+ uri = uri.replace(':', '://')
+
+ try:
+ uri = urlparse(uri)
+ except NameError:
+ # delayed import
+ from urlparse import urlparse, parse_qs
+ uri = urlparse(uri)
+
+ result = {'address': uri.netloc}
+
+ if uri.path.startswith('?'):
+ params = parse_qs(uri.path[1:])
+ else:
+ params = parse_qs(uri.path)
+
+ for k,v in params.items():
+ if k in ('amount', 'label', 'message', 'size'):
+ result[k] = v[0]
+
+ return result
+
+ def delete_imported_key(self, addr):
+ self.wallet.delete_imported_key(addr)
+ self.update_receive_tab()
+ self.update_history_tab()
+
+ def delete_pending_account(self, k):
+ self.wallet.delete_pending_account(k)
+ self.update_receive_tab()
+
+ def get_sendable_balance(self):
+ return sum(sum(self.wallet.get_addr_balance(a))
+ for a in self.get_payment_sources())
+
+
+ def get_payment_sources(self):
+ if self.pay_from:
+ return self.pay_from
+ else:
+ return self.wallet.get_account_addresses(self.current_account)
+
+
+ def send_from_addresses(self, addrs):
+ self.set_pay_from( addrs )
+ tabs = self.tabs
+ screen_send = tabs.ids.screen_send
+ self.tabs.setCurrentIndex(1)
+
+
+ def payto(self, addr):
+ if not addr:
+ return
+ label = self.wallet.labels.get(addr)
+ m_addr = label + ' <' + addr + '>' if label else addr
+ self.tabs.setCurrentIndex(1)
+ self.payto_e.setText(m_addr)
+ self.amount_e.setFocus()
+
+
+ def delete_contact(self, x):
+ if self.question(_("Do you want to remove") +
+ " %s "%x +
+ _("from your list of contacts?")):
+ self.wallet.delete_contact(x)
+ self.wallet.set_label(x, None)
+ self.update_history_tab()
+ self.update_contacts_tab()
+ self.update_completions()
+
+ def show_error(self, error, width='200dp', pos=None, arrow_pos=None,
+ exit=False, icon='atlas://gui/kivy/theming/light/error', duration=0,
+ modal=False):
''' Show a error Message Bubble.
'''
- self.show_info_bubble(
- text=error,
- icon=icon,
- width=width,
- pos=pos or Window.center,
- arrow_pos=arrow_pos,
- exit=exit,
- duration=duration,
- modal=modal)
-
- def show_info(self, error,
- width='200dp',
- pos=None,
- arrow_pos=None,
- exit=False,
- duration=0,
- modal=False):
+ self.show_info_bubble( text=error, icon=icon, width=width,
+ pos=pos or Window.center, arrow_pos=arrow_pos, exit=exit,
+ duration=duration, modal=modal)
+
+ def show_info(self, error, width='200dp', pos=None, arrow_pos=None,
+ exit=False, duration=0, modal=False):
''' Show a Info Message Bubble.
'''
self.show_error(error, icon='atlas://gui/kivy/theming/light/error',
- duration=duration,
- modal=modal,
- exit=exit,
- pos=pos,
- arrow_pos=arrow_pos)
-
- def show_info_bubble(self,
- text=_('Hello World'),
- pos=(0, 0),
- duration=0,
- arrow_pos='bottom_mid',
- width=None,
- icon='',
- modal=False,
- exit=False):
+ duration=duration, modal=modal, exit=exit, pos=pos,
+ arrow_pos=arrow_pos)
+
+ def show_info_bubble(self, text=_('Hello World'), pos=None, duration=0,
+ arrow_pos='bottom_mid', width=None, icon='', modal=False, exit=False):
'''Method to show a Information Bubble
.. parameters::
t@@ -769,13 +1242,13 @@ class ElectrumWindow(App):
width: width of the Bubble
arrow_pos: arrow position for the bubble
'''
-
info_bubble = self.info_bubble
if not info_bubble:
- info_bubble = self.info_bubble = InfoBubble()
+ info_bubble = self.info_bubble = Factory.InfoBubble()
+ win = Window
if info_bubble.parent:
- Window.remove_widget(info_bubble
+ win.remove_widget(info_bubble
if not info_bubble.modal else
info_bubble._modal_view)
t@@ -794,7 +1267,6 @@ class ElectrumWindow(App):
info_bubble.show_arrow = False
img.allow_stretch = True
info_bubble.dim_background = True
- pos = (Window.center[0], Window.center[1] - info_bubble.center[1])
info_bubble.background_image = 'atlas://gui/kivy/theming/light/card'
else:
info_bubble.fs = False
t@@ -805,4 +1277,6 @@ class ElectrumWindow(App):
info_bubble.dim_background = False
info_bubble.background_image = 'atlas://data/images/defaulttheme/bubble'
info_bubble.message = text
- info_bubble.show(pos, duration, width, modal=modal, exit=exit)
+ if not pos:
+ pos = (win.center[0], win.center[1] - (info_bubble.height/2))
+ info_bubble.show(pos, duration, width, modal=modal, exit=exit)
+\ No newline at end of file
DIR diff --git a/gui/kivy/plugins/exchange_rate.py b/gui/kivy/plugins/exchange_rate.py
t@@ -6,13 +6,18 @@ This module is responsible for getting the conversion rates from different
bitcoin exchanges.
'''
+import decimal
+import json
+
from kivy.network.urlrequest import UrlRequest
from kivy.event import EventDispatcher
from kivy.properties import (OptionProperty, StringProperty, AliasProperty,
ListProperty)
from kivy.clock import Clock
-import decimal
-import json
+from kivy.cache import Cache
+
+# Register local cache
+Cache.register('history_rate', timeout=220)
EXCHANGES = ["BitcoinAverage",
"BitcoinVenezuela",
t@@ -25,27 +30,32 @@ EXCHANGES = ["BitcoinAverage",
"LocalBitcoins",
"Winkdex"]
+HISTORY_EXCHNAGES = ['Coindesk',
+ 'Winkdex',
+ 'BitcoinVenezuela']
+
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', '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': '$',
- 'ERN': 'Nfk', 'ETB': 'Br', 'KYD': '$', 'USD': '$', 'CLP': '$',
- 'HRK': 'kn', 'CUP':'₱', 'CZK': 'Kč', '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': '₪', 'COP': '$',
- 'JMD': 'J$', 'JPY': '¥', 'JEP': '£', 'KZT': 'лв', 'KPW': '₩',
- 'KRW': '₩', 'KGS': 'лв', 'LAK': '₭', 'LVL': 'Ls', 'CNY': '¥'}
+ symbols = {'ALL': u'Lek', 'AED': u'د.إ', 'AFN':u'؋', 'ARS': u'$',
+ 'AMD': u'֏', 'AWG': u'ƒ', 'ANG': u'ƒ', 'AOA': u'Kz', 'BDT': u'৳',
+ 'BHD': u'BD', 'BIF': u'FBu', 'BTC': u'BTC', 'BTN': u'Nu', 'CDF': u'FC',
+ 'CHF': u'CHF', 'CLF': u'UF', 'CLP':u'$', 'CVE': u'$', 'DJF':u'Fdj',
+ 'DZD': u'دج', 'AUD': u'$', 'AZN': u'ман', 'BSD': u'$', 'BBD': u'$',
+ 'BYR': u'p', 'CRC': u'₡', 'BZD': u'BZ$', 'BMD': u'$', 'BOB': u'$b',
+ 'BAM': u'KM', 'BWP': u'P', 'BGN': 'uлв', 'BRL': u'R$', 'BND': u'$',
+ 'KHR': u'៛', 'CAD': u'$', 'ERN': u'Nfk', 'ETB': u'Br', 'KYD': u'$',
+ 'USD': u'$', 'CLP': u'$', 'HRK': u'kn', 'CUP': u'₱', 'CZK': u'Kč',
+ 'DKK': u'kr', 'DOP': u'RD$', 'XCD': u'$', 'EGP': u'£', 'SVC': u'$' ,
+ 'EEK': u'kr', 'EUR': u'€', u'FKP': u'£', 'FJD': u'$', 'GHC': u'¢',
+ 'GIP': u'£', 'GTQ': u'Q', 'GBP': u'£', 'GYD': u'$', 'HNL': u'L',
+ 'HKD': u'$', 'HUF': u'Ft', 'ISK': u'kr', 'INR': u'₹', 'IDR': u'Rp',
+ 'IRR': u'﷼', 'IMP': '£', 'ILS': '₪', 'COP': '$', 'JMD': u'J$',
+ 'JPY': u'¥', 'JEP': u'£', 'KZT': u'лв', 'KPW': u'₩', 'KRW': u'₩',
+ 'KGS': u'лв', 'LAK': u'₭', 'LVL': u'Ls', 'CNY': u'¥'}
_use_exchange = OptionProperty('Blockchain', options=EXCHANGES)
'''This is the exchange to be used for getting the currency exchange rates
t@@ -56,23 +66,16 @@ class Exchanger(EventDispatcher):
'''
def _set_currency(self, value):
- exchanger = self.exchanger
+ value = str(value)
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'
+ self._currency = value
+ self.parent.electrum_config.set_key('currency', value, True)
def _get_currency(self):
- try:
- self._currency = self.electrum_config.get('currency', 'EUR')
- except AttributeError:
- pass
- finally:
- return self._currency
+ self._currency = self.parent.electrum_config.get('currency', 'EUR')
+ return self._currency
currency = AliasProperty(_get_currency, _set_currency, bind=('_currency',))
t@@ -104,6 +107,7 @@ class Exchanger(EventDispatcher):
self.parent = parent
self.quote_currencies = None
self.exchanges = EXCHANGES
+ self.history_exchanges = HISTORY_EXCHNAGES
def exchange(self, btc_amount, quote_currency):
if self.quote_currencies is None:
t@@ -115,10 +119,40 @@ class Exchanger(EventDispatcher):
return btc_amount * decimal.Decimal(quote_currencies[quote_currency])
+ def get_history_rate(self, item, btc_amt, mintime, maxtime):
+ def on_success(request, response):
+ response = json.loads(response)
+
+ try:
+ hrate = response['bpi'][mintime]
+ hrate = abs(btc_amt) * decimal.Decimal(hrate)
+ Cache.append('history_rate', uid, hrate)
+ except KeyError:
+ hrate = 'not found'
+
+ self.parent.set_history_rate(item, hrate)
+
+ # Check local cache before getting data from remote
+ exchange = 'coindesk'
+ uid = '{}:{}'.format(exchange, mintime)
+ hrate = Cache.get('history_rate', uid)
+
+ if hrate:
+ return hrate
+
+ req = UrlRequest(url='https://api.coindesk.com/v1/bpi/historical'
+ '/close.json?start={}&end={}'
+ .format(mintime, maxtime)
+ ,on_success=on_success, timeout=15)
+ return None
+
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.
'''
+ if not self.parent.network or not self.parent.network.is_connected():
+ return
+
update_rates = {
"BitcoinAverage": self.update_ba,
"BitcoinVenezuela": self.update_bv,
t@@ -268,7 +302,7 @@ class Exchanger(EventDispatcher):
for r in response:
quote_currencies[r] = _lookup_rate(response, r)
self.quote_currencies = quote_currencies
- except KeyError:
+ except KeyError, TypeError:
pass
self.parent.set_currencies(quote_currencies)
t@@ -329,9 +363,8 @@ class Exchanger(EventDispatcher):
timeout=5)
def start(self):
- # check rates every few seconds
self.update_rate(0)
- # check every few seconds
+ # check every 20 seconds
Clock.unschedule(self.update_rate)
Clock.schedule_interval(self.update_rate, 20)
DIR diff --git a/gui/kivy/qr_scanner/__init__.py b/gui/kivy/qr_scanner/__init__.py
t@@ -7,69 +7,23 @@ from collections import namedtuple
from kivy.uix.anchorlayout import AnchorLayout
from kivy.core import core_select_lib
+from kivy.metrics import dp
from kivy.properties import ListProperty, BooleanProperty
from kivy.factory import Factory
-def encode_uri(addr, amount=0, label='', message='', size='',
- currency='btc'):
- ''' Convert to BIP0021 compatible URI
- '''
- uri = 'bitcoin:{}'.format(addr)
- first = True
- if amount:
- uri += '{}amount={}'.format('?' if first else '&', amount)
- first = False
- if label:
- uri += '{}label={}'.format('?' if first else '&', label)
- first = False
- if message:
- uri += '{}?message={}'.format('?' if first else '&', message)
- first = False
- if size:
- uri += '{}size={}'.format('?' if not first else '&', size)
- return uri
-
-def decode_uri(uri):
- if ':' not in uri:
- # It's just an address (not BIP21)
- return {'address': uri}
-
- if '//' not in uri:
- # Workaround for urlparse, it don't handle bitcoin: URI properly
- uri = uri.replace(':', '://')
-
- try:
- uri = urlparse(uri)
- except NameError:
- # delayed import
- from urlparse import urlparse, parse_qs
- uri = urlparse(uri)
-
- result = {'address': uri.netloc}
-
- if uri.path.startswith('?'):
- params = parse_qs(uri.path[1:])
- else:
- params = parse_qs(uri.path)
-
- for k,v in params.items():
- if k in ('amount', 'label', 'message', 'size'):
- result[k] = v[0]
-
- return result
-
-
class ScannerBase(AnchorLayout):
''' Base implementation for camera based scanner
'''
- camera_size = ListProperty([320, 240])
+ camera_size = ListProperty([320, 240] if dp(1) < 2 else [640, 480])
symbols = ListProperty([])
# XXX can't work now, due to overlay.
show_bounds = BooleanProperty(False)
+ running = BooleanProperty(False)
+
Qrcode = namedtuple('Qrcode',
['type', 'data', 'bounds', 'quality', 'count'])
DIR diff --git a/gui/kivy/qr_scanner/scanner_android.py b/gui/kivy/qr_scanner/scanner_android.py
t@@ -88,7 +88,7 @@ class SurfaceHolderCallback(PythonJavaClass):
def __init__(self, callback):
super(SurfaceHolderCallback, self).__init__()
self.callback = callback
-
+
@java_method('(Landroid/view/SurfaceHolder;III)V')
def surfaceChanged(self, surface, fmt, width, height):
self.callback(fmt, width, height)
t@@ -96,7 +96,7 @@ class SurfaceHolderCallback(PythonJavaClass):
@java_method('(Landroid/view/SurfaceHolder;)V')
def surfaceCreated(self, surface):
pass
-
+
@java_method('(Landroid/view/SurfaceHolder;)V')
def surfaceDestroyed(self, surface):
pass
t@@ -170,6 +170,7 @@ class AndroidCamera(Widget):
@run_on_ui_thread
def stop(self):
+ self.running = False
if self._android_camera is None:
return
self._android_camera.setPreviewCallback(None)
t@@ -179,6 +180,7 @@ class AndroidCamera(Widget):
@run_on_ui_thread
def start(self):
+ self.running = True
if self._android_camera is not None:
return
t@@ -196,6 +198,9 @@ class AndroidCamera(Widget):
# attach the android surfaceview to our android widget holder
self._holder.view = self._android_surface
+ # set orientation
+ self._android_camera.setDisplayOrientation(90)
+
def _on_surface_changed(self, fmt, width, height):
# internal, called when the android SurfaceView is ready
# FIXME if the size is not handled by the camera, it will failed.
DIR diff --git a/gui/kivy/qrcodewidget.py b/gui/kivy/qrcodewidget.py
t@@ -1,179 +0,0 @@
-''' Kivy Widget that accepts data and displas qrcode
-'''
-
-from threading import Thread
-from functools import partial
-
-from kivy.uix.floatlayout import FloatLayout
-
-from kivy.graphics.texture import Texture
-from kivy.properties import StringProperty
-from kivy.properties import ObjectProperty, StringProperty, ListProperty,\
- BooleanProperty
-from kivy.lang import Builder
-from kivy.clock import Clock
-
-try:
- import qrcode
-except ImportError:
- sys.exit("Error: qrcode does not seem to be installed. Try 'sudo pip install qrcode'")
-
-
-
-Builder.load_string('''
-<QRCodeWidget>
- on_parent: if args[1]: qrimage.source = self.loading_image
- canvas.before:
- # Draw white Rectangle
- Color:
- rgba: root.background_color
- Rectangle:
- size: self.size
- pos: self.pos
- canvas.after:
- Color:
- rgba: .5, .5, .5, 1 if root.show_border else 0
- Line:
- width: dp(1.333)
- points:
- dp(2), dp(2),\
- self.width - dp(2), dp(2),\
- self.width - dp(2), self.height - dp(2),\
- dp(2), self.height - dp(2),\
- dp(2), dp(2)
- Image
- id: qrimage
- pos_hint: {'center_x': .5, 'center_y': .5}
- allow_stretch: True
- size_hint: None, None
- size: root.width * .9, root.height * .9
-''')
-
-class QRCodeWidget(FloatLayout):
-
- show_border = BooleanProperty(True)
- '''Whether to show border around the widget.
-
- :data:`show_border` is a :class:`~kivy.properties.BooleanProperty`,
- defaulting to `True`.
- '''
-
- data = StringProperty(None, allow_none=True)
- ''' Data using which the qrcode is generated.
-
- :data:`data` is a :class:`~kivy.properties.StringProperty`, defaulting to
- `None`.
- '''
-
- background_color = ListProperty((1, 1, 1, 1))
- ''' Background color of the background of the widget.
-
- :data:`background_color` is a :class:`~kivy.properties.ListProperty`,
- defaulting to `(1, 1, 1, 1)`.
- '''
-
- loading_image = StringProperty('gui/kivy/theming/loading.gif')
-
- def __init__(self, **kwargs):
- super(QRCodeWidget, self).__init__(**kwargs)
- self.addr = None
- self.qr = None
- self._qrtexture = None
-
- def on_data(self, instance, value):
- if not (self.canvas or value):
- return
- img = self.ids.get('qrimage', None)
-
- if not img:
- # if texture hasn't yet been created delay the texture updation
- Clock.schedule_once(lambda dt: self.on_data(instance, value))
- return
- img.anim_delay = .25
- img.source = self.loading_image
- Thread(target=partial(self.generate_qr, value)).start()
-
- def generate_qr(self, value):
- self.set_addr(value)
- self.update_qr()
-
- def set_addr(self, addr):
- if self.addr == addr:
- return
- MinSize = 210 if len(addr) < 128 else 500
- self.setMinimumSize((MinSize, MinSize))
- self.addr = addr
- self.qr = None
-
- def update_qr(self):
- if not self.addr and self.qr:
- return
- QRCode = qrcode.QRCode
- L = qrcode.constants.ERROR_CORRECT_L
- addr = self.addr
- try:
- self.qr = qr = QRCode(
- version=None,
- error_correction=L,
- box_size=10,
- border=0,
- )
- qr.add_data(addr)
- qr.make(fit=True)
- except Exception as e:
- print e
- self.qr=None
- self.update_texture()
-
- def setMinimumSize(self, size):
- # currently unused, do we need this?
- self._texture_size = size
-
- def _create_texture(self, k, dt):
- self._qrtexture = texture = Texture.create(size=(k,k), colorfmt='rgb')
- # don't interpolate texture
- texture.min_filter = 'nearest'
- texture.mag_filter = 'nearest'
-
- def update_texture(self):
- if not self.addr:
- return
-
- matrix = self.qr.get_matrix()
- k = len(matrix)
- # create the texture in main UI thread otherwise
- # this will lead to memory corruption
- Clock.schedule_once(partial(self._create_texture, k), -1)
- buff = []
- bext = buff.extend
- cr, cg, cb, ca = self.background_color[:]
- cr, cg, cb = cr*255, cg*255, cb*255
-
- for r in range(k):
- for c in range(k):
- bext([0, 0, 0] if matrix[r][c] else [cr, cg, cb])
-
- # then blit the buffer
- buff = ''.join(map(chr, buff))
- # update texture in UI thread.
- Clock.schedule_once(lambda dt: self._upd_texture(buff))
-
- def _upd_texture(self, buff):
- texture = self._qrtexture
- if not texture:
- # if texture hasn't yet been created delay the texture updation
- Clock.schedule_once(lambda dt: self._upd_texture(buff))
- return
- texture.blit_buffer(buff, colorfmt='rgb', bufferfmt='ubyte')
- img =self.ids.qrimage
- img.anim_delay = -1
- img.texture = texture
- img.canvas.ask_update()
-
-if __name__ == '__main__':
- from kivy.app import runTouchApp
- import sys
- data = str(sys.argv[1:])
- runTouchApp(QRCodeWidget(data=data))
-
-
DIR diff --git a/gui/kivy/screens.py b/gui/kivy/screens.py
t@@ -1,105 +0,0 @@
-from kivy.app import App
-from kivy.uix.screenmanager import Screen
-from kivy.properties import ObjectProperty
-from kivy.clock import Clock
-
-
-class CScreen(Screen):
-
- __events__ = ('on_activate', 'on_deactivate')
-
- action_view = ObjectProperty(None)
-
- def _change_action_view(self):
- app = App.get_running_app()
- action_bar = app.root.manager.current_screen.ids.action_bar
- _action_view = self.action_view
-
- if (not _action_view) or _action_view.parent:
- return
- action_bar.clear_widgets()
- action_bar.add_widget(_action_view)
-
- def on_activate(self):
- Clock.schedule_once(lambda dt: self._change_action_view())
-
- def on_deactivate(self):
- Clock.schedule_once(lambda dt: self._change_action_view())
-
-
-class ScreenDashboard(CScreen):
-
- tab = ObjectProperty(None)
-
- def show_tx_details(
- self, date, address, amount, amount_color, balance,
- tx_hash, conf, quote_text):
-
- ra_dialog = RecentActivityDialog()
-
- ra_dialog.address = address
- ra_dialog.amount = amount
- ra_dialog.amount_color = amount_color
- ra_dialog.confirmations = conf
- ra_dialog.quote_text = quote_text
- date_time = date.split()
- if len(date_time) == 2:
- ra_dialog.date = date_time[0]
- ra_dialog.time = date_time[1]
- ra_dialog.status = 'Validated'
- else:
- ra_dialog.date = date_time
- ra_dialog.status = 'Pending'
- ra_dialog.tx_hash = tx_hash
-
- app = App.get_running_app()
- main_gui = app.gui.main_gui
- tx_hash = tx_hash
- tx = app.wallet.transactions.get(tx_hash)
-
- if tx_hash in app.wallet.transactions.keys():
- is_relevant, is_mine, v, fee = app.wallet.get_tx_value(tx)
- conf, timestamp = app.wallet.verifier.get_confirmations(tx_hash)
- #if timestamp:
- # time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
- #else:
- # time_str = 'pending'
- else:
- is_mine = False
-
- ra_dialog.is_mine = is_mine
-
- if is_mine:
- if fee is not None:
- ra_dialog.fee = main_gui.format_amount(fee)
- else:
- ra_dialog.fee = 'unknown'
-
- ra_dialog.open()
-
-
-class ScreenPassword(Screen):
-
- __events__ = ('on_release', 'on_deactivate', 'on_activate')
-
- def on_activate(self):
- app = App.get_running_app()
- action_bar = app.root.main_screen.ids.action_bar
- action_bar.add_widget(self._action_view)
-
- def on_deactivate(self):
- self.ids.password.text = ''
-
- def on_release(self, *args):
- pass
-
-class ScreenSend(CScreen):
- pass
-
-class ScreenReceive(CScreen):
- pass
-
-class ScreenContacts(CScreen):
-
- def add_new_contact(self):
- NewContactDialog().open()
DIR diff --git a/gui/kivy/statusbar.py b/gui/kivy/statusbar.py
t@@ -1,7 +0,0 @@
-from kivy.uix.boxlayout import BoxLayout
-from kivy.properties import StringProperty
-
-
-class StatusBar(BoxLayout):
-
- text = StringProperty('')
DIR diff --git a/gui/kivy/textinput.py b/gui/kivy/textinput.py
t@@ -1,14 +0,0 @@
-from kivy.uix.textinput import TextInput
-from kivy.properties import OptionProperty
-
-class ELTextInput(TextInput):
-
- def insert_text(self, substring, from_undo=False):
- if not from_undo:
- if self.input_type == 'numbers':
- numeric_list = map(str, range(10))
- if '.' not in self.text:
- numeric_list.append('.')
- if substring not in numeric_list:
- return
- super(ELTextInput, self).insert_text(substring, from_undo=from_undo)
DIR diff --git a/gui/kivy/theming/light-0.png b/gui/kivy/theming/light-0.png
Binary files differ.
DIR diff --git a/gui/kivy/theming/light.atlas b/gui/kivy/theming/light.atlas
t@@ -1 +1 @@
parazyd.org:70 /git/electrum/commit/c121c1aa4e2d20825d4eafc201144e84df3129dd.gph:4082: line too long