tthe rest of the installation wizard +numerous small fixes - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit f185906950ba71ffb3f27f794595d8ce09a045a6 DIR parent 30126c544b81bcb8ac6a510da9c9d42acf82ee28 HTML Author: qua-non <akshayaurora@gmail.com> Date: Thu, 20 Feb 2014 00:24:37 +0530 tthe rest of the installation wizard +numerous small fixes Diffstat: M gui/kivy/dialog.py | 62 +++++++++++++++++++++---------- M gui/kivy/drawer.py | 17 ++++++++++++----- M gui/kivy/installwizard.py | 138 ++++++++++++++++++++++++++----- M gui/kivy/main.kv | 129 +++++++++++++++++++++++-------- M gui/kivy/main_window.py | 13 ++++++++----- M gui/kivy/theming/light-0.png | 0 D gui/kivy/theming/light-1.png | 0 M gui/kivy/theming/light.atlas | 4 ++-- M gui/kivy/theming/light/electrum_ic… | 0 M gui/kivy/theming/light/nfc_clock.p… | 0 10 files changed, 277 insertions(+), 86 deletions(-) --- DIR diff --git a/gui/kivy/dialog.py b/gui/kivy/dialog.py t@@ -131,20 +131,24 @@ class InfoBubble(Bubble): ''' Allow bubble to be hidden on touch. ''' + exit = BooleanProperty(False) + ''' exit app after bubble is closes + ''' + dim_background = BooleanProperty(False) ''' Whether to draw a background on the windows behind the bubble ''' def on_touch_down(self, touch): if self.modal: - return + return True self.hide() if self.collide_point(*touch.pos): return True - def show(self, pos, duration, width=None, modal=False): + def show(self, pos, duration, width=None, modal=False, exit=False): '''Animate the bubble into position''' - self.modal = modal + self.modal, self.exit = modal, exit if width: self.width = width Window.add_widget(self) t@@ -177,6 +181,11 @@ class InfoBubble(Bubble): ''' def on_stop(*l): Window.remove_widget(self) + if self.exit: + App.get_running_app().stop() + import sys + sys.exit() + anim = Animation(opacity=0, d=.25) anim.bind(on_complete=on_stop) anim.cancel_all(self) t@@ -412,6 +421,23 @@ class CreateAccountDialog(EventsDialog): 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('') t@@ -436,30 +462,25 @@ class InitSeedDialog(CreateAccountDialog): self._back = None super(InitSeedDialog, self).close() -class CreateRestoreDialog(CreateAccountDialog): - ''' Initial Dialog for creating or restoring seed''' +class VerifySeedDialog(CreateAccountDialog): + + pass + +class RestoreSeedDialog(CreateAccountDialog): 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') + 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 close(self): if self._back in app.navigation_higherarchy: app.navigation_higherarchy.pop() self._back = None - super(CreateRestoreDialog, self).close() - - -class VerifySeedDialog(CreateAccountDialog): - - pass - -class RestoreSeedDialog(CreateAccountDialog): - - pass + super(RestoreSeedDialog, self).close() class NewContactDialog(Popup): t@@ -508,11 +529,12 @@ class ChangePasswordDialog(CreateAccountDialog): message = StringProperty(_('Empty Message')) '''Message to be displayed.''' - mode = OptionProperty('new', options=('new', 'confirm', 'create')) + mode = OptionProperty('new', + options=('new', 'confirm', 'create', 'restore')) ''' Defines the mode of the password dialog.''' def validate_new_password(self): - self.ids.confirm.dispatch('on_release') + self.ids.next.dispatch('on_release') def on_parent(self, instance, value): if value: DIR diff --git a/gui/kivy/drawer.py b/gui/kivy/drawer.py t@@ -31,7 +31,7 @@ class Drawer(StencilView): and defaults to 200 (milliseconds) ''' - scroll_distance = NumericProperty('4dp') + 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. t@@ -68,6 +68,11 @@ class Drawer(StencilView): 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 t@@ -91,10 +96,9 @@ class Drawer(StencilView): return def on_touch_move(self, touch): - global app - if not app: - from kivy.app import App - app = App.get_running_app() + if not touch.grab_current: + return + # skip on tablet mode if app.ui_mode[0] == 't': return super(Drawer, self).on_touch_move(touch) t@@ -124,6 +128,9 @@ class Drawer(StencilView): 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) DIR diff --git a/gui/kivy/installwizard.py b/gui/kivy/installwizard.py t@@ -1,14 +1,12 @@ from electrum import Wallet from electrum.i18n import _ -from electrum_gui.kivy.dialog import (CreateRestoreDialog, InitSeedDialog, - ChangePasswordDialog) from kivy.app import App from kivy.uix.widget import Widget from kivy.core.window import Window from kivy.clock import Clock -#from seed_dialog import SeedDialog +from electrum_gui.kivy.dialog import CreateRestoreDialog #from network_dialog import NetworkDialog #from util import * #from amountedit import AmountEdit t@@ -33,13 +31,18 @@ class InstallWizard(Widget): def waiting_dialog(self, task, msg= _("Electrum is generating your addresses," - " please wait.")): + " please wait."), + on_complete=None): + def target(): + # run your threaded function task() + # on completion hide message Clock.schedule_once(lambda dt: - app.show_info_bubble(text="Complete", duration=.5, - icon='atlas://gui/kivy/theming/light/important', - pos=Window.center, width='200dp', arrow_pos=None)) + app.show_info_bubble(text="Complete", arrow_pos=None)) + # 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', t@@ -66,13 +69,58 @@ class InstallWizard(Widget): self.change_password_dialog(wallet=wallet) elif button == dialog.ids.restore: # restore - wallet.init_seed(None) - self.restore_seed_dialog() + self.restore_seed_dialog(wallet) #elif 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!")) + return + + try: + wallet.init_seed(seed) + except Exception: + import traceback + traceback.print_exc(file=sys.stdout) + app.show_error(_('No account tied to this seedphrase'), exit=True) + return + + _dlg.close() + self.change_password_dialog(wallet=wallet, mode='restore') + return + + from pudb import set_trace; set_trace() + wallet = self.wallet + #is_restore = bool(_dlg.__class__ == RestoreSeedDialog) + + # Restore + if len(seed) == 128: + wallet.seed = '' + wallet.init_sequence(str(seed)) + else: + wallet.seed = '' + wallet.init_seed(str(seed)) + wallet.save_seed() + + return self.change_network_dialog() + def init_seed_dialog(self, wallet=None, instance=None, password=None, wallet_name=None): # renamed from show_seed() t@@ -125,15 +173,16 @@ class InstallWizard(Widget): Clock.schedule_once(lambda dt: app.show_error(err)) wallet.synchronize() # generate first addresses offline - self.waiting_dialog(partial(create, password)) - + self.waiting_dialog(partial(create, password), + on_complete=self.load_network) + 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): + 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)""" t@@ -154,13 +203,14 @@ class InstallWizard(Widget): msg = _("Please choose a password to encrypt your wallet keys.") +\ '\n' + _("Leave these fields empty if you want to disable" + \ " encryption.") - mode = 'create' 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': + return _dlg.close() if not instance: CreateRestoreDialog( t@@ -185,25 +235,29 @@ class InstallWizard(Widget): ti_password.focus = True return app.show_error(_('Passwords do not match')) + if mode == 'restore': + _dlg.close() + wallet.save_seed(new_password) + self.load_network(wallet, mode='restore') + return if not instance: + # create _dlg.close() - self.init_seed_dialog(password=new_password, + self.load_network(wallet, mode='create') + return self.init_seed_dialog(password=new_password, wallet=wallet, wallet_name=wallet_name) - return try: seed = wallet.decode_seed(password) except BaseException: - return MessageBoxError( - message=_('Incorrect Password')).open() + return app.show_error(_('Incorrect Password')) # test carefully try: wallet.update_password(seed, password, new_password) except BaseException: - return MessageBoxExit( - message=_('Failed to update password')).open() + return app.show_error(_('Failed to update password'), exit=True) else: app.show_info_bubble( text=_('Password successfully updated'), duration=1, t@@ -213,12 +267,56 @@ class InstallWizard(Widget): if instance is None: # in initial phase self.load_wallet() - self.app.gui.main_gui.update_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=None): + #if not self.config.get('server'): + if not self.network: + return wallet.start_threads(self.network) + + if not self.network.interfaces: + app.show_error(_('You are offline')) + self.network.stop() + self.network = None + return wallet.start_threads(self.network) + + if mode not in ('restore', 'create'): + self.network_dialog() + return wallet.start_threads(self.network) + + self.config.set_key('auto_cycle', True, True) + wallet.start_threads(self.network) + + 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_bubble( + text=_("This wallet was restored offline. It may contain" + " more addresses than displayed."), + width='200dp', + pos=Window.center) + return + + if wallet.is_found(): + app.show_info_bubble(_("Recovery successful"), + width='200dp', + pos=Window.center) + else: + app.show_info_bubble(_("No transactions found for this seed"), + width='200dp', + pos=Window.center) + + self.waiting_dialog(lambda: wallet.restore(get_text), + on_complete=on_complete) + def on_wizard_complete(self, instance, wallet): pass DIR diff --git a/gui/kivy/main.kv b/gui/kivy/main.kv t@@ -92,9 +92,9 @@ 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)) - on_touch_down: self.hide() BoxLayout: padding: '5dp' + spacing: '5dp' Widget: size_hint: None, 1 width: '4dp' if root.fs else '2dp' t@@ -179,8 +179,8 @@ size_hint: 1, None height: grid_logo.height/2.5 if self.opacity else 0 Widget: - size_hint: 1, None - height: '5dp' + size_hint: None, None + size: '5dp', '5dp' GridLayout: cols: 1 id: crcontent t@@ -219,6 +219,53 @@ # 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: t@@ -281,37 +328,50 @@ <ChangePasswordDialog> padding: '7dp' - 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 - CreateAccountTextInput: - id: ti_password - hint_text: 'Enter old pincode' + GridLayout: size_hint_y: None - height: 0 if self.disabled else '38sp' - password: True - disabled: True if root.mode in ('new', 'create') else False - opacity: 0 if self.disabled else 1 - multiline: False - on_text_validate: - #root.validate_old_password() - ti_new_password.focus = True - CreateAccountTextInput: - id: ti_new_password - hint_text: 'Enter new pincode' - multiline: False - password: True - on_text_validate: ti_confirm_password.focus = True - CreateAccountTextInput: - id: ti_confirm_password - hint_text: 'Confirm pincode' - password: True - multiline: False - on_text_validate: root.validate_new_passowrd() + 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 t@@ -322,9 +382,10 @@ id: back text: _('Back') root: root + disabled: True if root.mode[0] == 'r' else self.disabled CreateAccountButtonGreen: id: next - text: _('Next') + text: _('Confirm') if root.mode[0] == 'r' else _('Next') root: root ############################################### DIR diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py t@@ -106,7 +106,7 @@ class ElectrumWindow(App): def on_start(self): Window.bind(size=self.on_size, on_keyboard=self.on_keyboard) - Window.bind(keyboard_height=self.on_keyboard_height) + #Window.bind(keyboard_height=self.on_keyboard_height) self.on_size(Window, Window.size) config = self.electrum_config storage = WalletStorage(config) t@@ -229,7 +229,8 @@ class ElectrumWindow(App): def show_error(self, error, width='200dp', pos=None, - arrow_pos=None): + arrow_pos=None, + exit=False): ''' Show a error Message Bubble. ''' self.show_info_bubble( t@@ -237,7 +238,8 @@ class ElectrumWindow(App): icon='atlas://gui/kivy/theming/light/error', width=width, pos=pos or Window.center, - arrow_pos=arrow_pos) + arrow_pos=arrow_pos, + exit=exit) def show_info_bubble(self, text=_('Hello World'), t@@ -246,7 +248,8 @@ class ElectrumWindow(App): arrow_pos='bottom_mid', width=None, icon='', - modal=False): + modal=False, + exit=False): '''Method to show a Information Bubble .. parameters:: t@@ -291,4 +294,4 @@ 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) + info_bubble.show(pos, duration, width, modal=modal, exit=exit) 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-1.png b/gui/kivy/theming/light-1.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/f185906950ba71ffb3f27f794595d8ce09a045a6.gph:649: line too long