commit 33c89b47d8d4222df812603959463affa7ca4a3e
parent 802b32d51ffcc825b6f02cda7c66771a24c63f55
Author: akshayaurora <akshayaurora@gmail.com>
Date: Sat, 21 Jun 2014 22:39:31 +0530
revert dynamic loading of screens and fix qrscanning
Diffstat:
13 files changed, 418 insertions(+), 788 deletions(-)
diff --git a/gui/kivy/Makefile b/gui/kivy/Makefile
@@ -9,7 +9,7 @@ apk:
# running pre build setup
@cp tools/buildozer.spec ../../buildozer.spec
# get aes.py
- @cd ../..; curl -O https://raw.github.com/devrandom/slowaes/master/python/aes.py
+ @cd ../..; curl -O -L https://raw.github.com/devrandom/slowaes/master/python/aes.py
# rename electrum to main.py
@mv ../../electrum ../../main.py
@-if [ ! -d "../../.buildozer" ];then \
diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py
@@ -21,6 +21,8 @@ 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')
+Factory.register('QrScannerDialog', module='electrum_gui.kivy.uix.dialogs.qr_scanner')
+
# delayed imports: for startup speed on android
notification = app = Decimal = ref = format_satoshis = is_valid = Builder = None
@@ -405,6 +407,8 @@ class ElectrumWindow(App):
def create_quote_text(self, btc_balance, mode='normal'):
'''
'''
+ if not self.exchanger:
+ return
quote_currency = self.exchanger.currency
quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
@@ -432,6 +436,7 @@ class ElectrumWindow(App):
return self.set_history_rate(item, rate)
+
def set_history_rate(self, item, rate):
'''
'''
@@ -831,7 +836,7 @@ class ElectrumWindow(App):
message = 'sending {} {} to {}'.format(\
app.base_unit, scrn.amount_e.text, r)
- confirm_fee = self.config.get('confirm_fee', 100000)
+ confirm_fee = self.config.get('confirm_amount', 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
@@ -913,8 +918,8 @@ class ElectrumWindow(App):
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
if(v > 0):
self.notify(
- _("{} new transaction received. {amount}s {unit}s").
- format( amount=self.format_amount(v),
+ _("{txs} new transaction received. {amount} {unit}").
+ format(txs=tx_amount, amount=self.format_amount(v),
unit=self.base_unit))
def copy(self, text):
diff --git a/gui/kivy/tools/buildozer.spec b/gui/kivy/tools/buildozer.spec
@@ -32,7 +32,7 @@ source.exclude_exts = spec
version = 1.9.8
# (list) Application requirements
-requirements = pil, qrcode, ecdsa, pbkdf2, pyopenssl, plyer==master, kivy==master
+requirements = pil, qrcode, ecdsa, pbkdf2, openssl, pyopenssl, plyer==master, kivy==master
# (str) Presplash of the application
presplash.filename = %(source.dir)s/gui/kivy/theming/splash.png
diff --git a/gui/kivy/ui_screens/screenreceive.kv b/gui/kivy/ui_screens/screenreceive.kv
@@ -1,129 +0,0 @@
-<ScreenReceiveContent@BoxLayout>
- opacity: 0
- padding: '12dp', '12dp', '12dp', '12dp'
- spacing: '12dp'
- mode: 'qr'
- orientation: 'vertical'
- SendReceiveToggle
- SendToggle:
- id: toggle_qr
- text: 'QR'
- state: 'down' if root.mode == 'qr' else 'normal'
- source: 'atlas://gui/kivy/theming/light/qrcode'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_address'
- on_release:
- if root.mode == 'qr': root.mode = 'nr'
- root.mode = 'qr'
- SendToggle:
- id: toggle_nfc
- text: 'NFC'
- state: 'down' if root.mode == 'nfc' else 'normal'
- source: 'atlas://gui/kivy/theming/light/nfc'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_nfc'
- on_release:
- if root.mode == 'nfc': root.mode = 'nr'
- root.mode = 'nfc'
- GridLayout:
- id: grid
- cols: 1
- #size_hint: 1, None
- #height: self.minimum_height
- SendReceiveCardTop
- height: '110dp'
- BoxLayout:
- size_hint: 1, None
- height: '42dp'
- rows: 1
- Label:
- color: amount_e.foreground_color
- bold: True
- text_size: self.size
- valign: 'bottom'
- font_size: '22sp'
- text: app.base_unit
- size_hint_x: .25
- ELTextInput:
- id: amount_e
- input_type: 'number'
- multiline: False
- bold: True
- font_size: '50sp'
- foreground_color: .308, .308, .308, 1
- background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
- pos_hint: {'top': 1.5}
- size_hint: .7, None
- height: '67dp'
- hint_text: 'Amount'
- text: '0.0'
- on_text_validate: payto_e.focus = True
- CardSeparator
- BoxLayout:
- size_hint: 1, None
- height: '32dp'
- spacing: '5dp'
- Label:
- id: lbl_quote
- font_size: '12dp'
- size_hint: .5, 1
- color: .761, .761, .761, 1
- #text: app.create_quote_text(Decimal(amount_e.text))
- text_size: self.size
- halign: 'left'
- valign: 'middle'
- Label:
- color: lbl_quote.color
- font_size: '12dp'
- text: 'Ask to scan the QR below'
- text_size: self.size
- halign: 'right'
- valign: 'middle'
- SendReceiveBlueBottom
- id: blue_bottom
- padding: '12dp', 0, '12dp', '12dp'
- WalletSelector:
- id: wallet_selection
- foreground_color: blue_bottom.foreground_color
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- CardSeparator
- opacity: wallet_selection.opacity
- color: blue_bottom.foreground_color
- AddressSelector:
- id: address_selection
- foreground_color: blue_bottom.foreground_color
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- on_text:
- if not args[1].startswith('Select'):\
- qr.data = app.encode_uri(self.text)
- CardSeparator
- opacity: address_selection.opacity
- color: blue_bottom.foreground_color
- Widget:
- size_hint_y: None
- height: dp(10)
- BoxLayout
- #size_hint: 1, None
- #height: '160dp' if app.expert_mode else '220dp'
- Widget
- QRCodeWidget:
- id: qr
- size_hint: None, 1
- width: self.height
- data: app.encode_uri(app.wallet.addresses()[0]) if app.wallet.addresses() else ''
- on_touch_down:
- if self.collide_point(*args[1].pos):\
- app.show_info_bubble(icon=self.ids.qrimage.texture, text='texture')
- Widget
- CreateAccountButtonGreen:
- background_color: (1, 1, 1, 1) if self.disabled else ((.258, .80, .388, 1) if self.state == 'normal' else (.203, .490, .741, 1))
- text: _('Goto next step') if app.wallet.seed else _('Create unsigned transaction')
- size_hint_y: None
- height: '38dp'
- disabled: True if wallet_selection.opacity == 0 else False
- on_release:
- message = 'sending {} {} to {}'.format(\
- app.base_unit, amount_e.text, payto_e.text)
- app.gui.main_gui.do_send(self, message=message)-
\ No newline at end of file
diff --git a/gui/kivy/ui_screens/screensend.kv b/gui/kivy/ui_screens/screensend.kv
@@ -1,187 +0,0 @@
-<TextInputSendBlue@TextInput>
- padding: '5dp'
- size_hint: 1, None
- height: '27dp'
- pos_hint: {'center_y':.5}
- multiline: False
- hint_text_color: self.foreground_color
- foreground_color: .843, .914, .972, 1
- background_color: 1, 1, 1, 1
- background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
- background_active: 'atlas://gui/kivy/theming/light/textinput_active'
-
-<ScreenSendContent@BoxLayout>
- opacity: 0
- padding: '12dp', '12dp', '12dp', '12dp'
- spacing: '12dp'
- orientation: 'vertical'
- mode: 'address'
- SendReceiveToggle:
- SendToggle:
- id: toggle_address
- text: 'ADDRESS'
- group: 'send_type'
- state: 'down' if root.mode == 'address' else 'normal'
- source: 'atlas://gui/kivy/theming/light/globe'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_address'
- on_release:
- if root.mode == 'address': root.mode = 'fc'
- root.mode = 'address'
- SendToggle:
- id: toggle_nfc
- text: 'NFC'
- group: 'send_type'
- state: 'down' if root.mode == 'nfc' else 'normal'
- source: 'atlas://gui/kivy/theming/light/nfc'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_nfc'
- on_release:
- if root.mode == 'nfc': root.mode = 'str'
- root.mode = 'nfc'
- GridLayout:
- id: grid
- cols: 1
- size_hint: 1, None
- height: self.minimum_height
- SendReceiveCardTop
- id: card_address
- BoxLayout
- size_hint: 1, None
- height: '42dp'
- rows: 1
- Label
- bold: True
- color: amount_e.foreground_color
- text_size: self.size
- valign: 'bottom'
- font_size: '22sp'
- text: app.base_unit
- size_hint_x: .25
- ELTextInput:
- id: amount_e
- input_type: 'number'
- multiline: False
- bold: True
- font_size: '50sp'
- foreground_color: .308, .308, .308, 1
- background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
- pos_hint: {'top': 1.5}
- size_hint: .7, None
- height: '67dp'
- hint_text: 'Amount'
- text: '0.0'
- on_text_validate: payto_e.focus = True
- CardSeparator
- BoxLayout:
- size_hint: 1, None
- height: '42dp'
- spacing: '5dp'
- Label:
- font_size: '12dp'
- color: lbl_fee.color
- text: app.gui.main_gui.create_quote_text(Decimal(amount_e.text)) if hasattr(app, 'gui') else '0'
- text_size: self.size
- halign: 'left'
- valign: 'middle'
- Label:
- id: lbl_fee
- color: .761, .761, .761, 1
- font_size: '12dp'
- text: '[b]{}[/b] of fee'.format(fee_e.value)
- text_size: self.size
- halign: 'right'
- valign: 'middle'
- IconButton:
- id: fee_e
- source: 'atlas://gui/kivy/theming/light/contact'
- text: str(self.value)
- value: .0005
- pos_hint: {'center_y': .5}
- size_hint: None, None
- size: '32dp', '32dp'
- on_release: print 'TODO'
- SendReceiveBlueBottom:
- id: blue_bottom
- size_hint: 1, None
- height: self.minimum_height
- BoxLayout
- size_hint: 1, None
- height: blue_bottom.item_height
- spacing: '5dp'
- Image:
- source: 'atlas://gui/kivy/theming/light/contact'
- size_hint: None, None
- size: '22dp', '22dp'
- pos_hint: {'center_y': .5}
- TextInputSendBlue:
- id: payto_e
- hint_text: "Enter Contact or adress"
- on_text_validate:
- Factory.Animation(opacity=1,\
- height=blue_bottom.item_height)\
- .start(message_selection)
- message_e.focus = True
- Widget:
- size_hint: None, None
- width: dp(2)
- height: qr.height
- pos_hint: {'center_y':.5}
- canvas.after:
- Rectangle:
- size: self.size
- pos: self.pos
- IconButton:
- id: qr
- source: 'atlas://gui/kivy/theming/light/qrcode'
- pos_hint: {'center_y': .5}
- size_hint: None, None
- size: '22dp', '22dp'
- CardSeparator
- opacity: message_selection.opacity
- color: blue_bottom.foreground_color
- BoxLayout:
- id: message_selection
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- spacing: '5dp'
- Image:
- source: 'atlas://gui/kivy/theming/light/pen'
- size_hint: None, None
- size: '22dp', '22dp'
- pos_hint: {'center_y': .5}
- TextInputSendBlue:
- id: message_e
- hint_text: 'Enter description here'
- on_text_validate:
- anim = Factory.Animation(opacity=1, height=blue_bottom.item_height)
- anim.start(wallet_selection)
- #anim.start(address_selection)
- CardSeparator
- opacity: wallet_selection.opacity
- color: blue_bottom.foreground_color
- WalletSelector:
- id: wallet_selection
- foreground_color: blue_bottom.foreground_color
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- CardSeparator
- opacity: address_selection.opacity
- color: blue_bottom.foreground_color
- AddressSelector:
- id: address_selection
- foreground_color: blue_bottom.foreground_color
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- CreateAccountButtonGreen:
- background_color: (1, 1, 1, 1) if self.disabled else ((.258, .80, .388, 1) if self.state == 'normal' else (.203, .490, .741, 1))
- text: _('Goto next step') if app.wallet.seed else _('Create unsigned transaction')
- size_hint_y: None
- height: '38dp'
- disabled: True if wallet_selection.opacity == 0 else False
- on_release:
- message = 'sending {} {} to {}'.format(\
- app.base_unit, amount_e.text, payto_e.text)
- app.gui.main_gui.do_send(self, message=message)
- Widget
diff --git a/gui/kivy/uix/dialogs/__init__.py b/gui/kivy/uix/dialogs/__init__.py
@@ -12,7 +12,7 @@ class AnimatedPopup(Factory.Popup):
''' An Animated Popup that animates in and out.
'''
- anim_duration = NumericProperty(.25)
+ anim_duration = NumericProperty(.36)
'''Duration of animation to be used
'''
diff --git a/gui/kivy/uix/dialogs/create_restore.py b/gui/kivy/uix/dialogs/create_restore.py
@@ -154,7 +154,7 @@ Builder.load_string('''
height: '110dp'
hint_text:
_('Enter your seedphrase')
- on_text: next.disabled = not bool(root._wizard.is_any(self))
+ on_text: root._trigger_check_seed()
Label:
font_size: '12sp'
text_size: self.width, None
@@ -446,6 +446,11 @@ class RestoreSeedDialog(CreateAccountDialog):
def __init__(self, **kwargs):
self._wizard = kwargs['wizard']
super(RestoreSeedDialog, self).__init__(**kwargs)
+ self._trigger_check_seed = Clock.create_trigger(self.check_seed)
+
+ def check_seed(self, dt):
+ self.ids.next.disabled = not bool(self._wizard.is_any(
+ self.ids.text_input_seed))
def on_parent(self, instance, value):
if value:
diff --git a/gui/kivy/uix/dialogs/new_contact.py b/gui/kivy/uix/dialogs/new_contact.py
@@ -13,10 +13,10 @@ class NewContactDialog(Factory.AnimatedPopup):
if not dlg:
dlg = Factory.QrScannerDialog()
Cache.append('electrum_widgets', 'QrScannerDialog', dlg)
- dlg.bind(on_release=self.on_release)
+ dlg.bind(on_complete=self.on_complete)
dlg.open()
- def on_release(self, instance, uri):
+ def on_complete(self, instance, uri):
self.new_contact(uri=uri)
def new_contact(self, uri={}):
diff --git a/gui/kivy/uix/dialogs/qr_scanner.py b/gui/kivy/uix/dialogs/qr_scanner.py
@@ -4,7 +4,9 @@ from kivy.lang import Builder
Factory.register('QRScanner', module='electrum_gui.kivy.qr_scanner')
-class QrScannerDialog(Factory.EventsDialog):
+class QrScannerDialog(Factory.AnimaterPopup):
+
+ __events__('on_complete', )
def on_symbols(self, instance, value):
instance.stop()
@@ -14,7 +16,7 @@ class QrScannerDialog(Factory.EventsDialog):
#label = uri.get('label', '')
#amount = uri.get('amount', 0.0)
#message = uir.get('message', '')
- self.dispatch('on_release', uri)
+ self.dispatch('on_omplete', uri)
Builder.load_string('''
diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py
@@ -41,76 +41,8 @@ class CScreen(Factory.Screen):
def on_deactivate(self):
Clock.schedule_once(lambda dt: self._change_action_view())
- def load_screen(self, screen_name):
- content = self.content
- if not content:
- Builder.load_file('gui/kivy/uix/ui_screens/{}.kv'.format(screen_name))
- if screen_name.endswith('send'):
- content = Factory.ScreenSendContent()
- elif screen_name.endswith('receive'):
- content = Factory.ScreenReceiveContent()
- content.ids.toggle_qr.state = 'down'
- self.content = content
- self.add_widget(content)
- Factory.Animation(opacity=1, d=.25).start(content)
- return
- if screen_name.endswith('receive'):
- content.mode = 'qr'
- else:
- content.mode = 'address'
-
-
-class EScreen(Factory.EffectWidget, CScreen):
-
- background_color = ListProperty((0.929, .929, .929, .929))
-
- speed = NumericProperty(0)
- effect_flex_scroll = '''
-uniform float speed;
-
-vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
-{{
- return texture2D(
- texture,
- vec2(tex_coords.x + sin(
- tex_coords.y * 3.1416 / .2 + 3.1416 / .5
- ) * speed, tex_coords.y));
-}}
-'''
- def __init__(self, **kwargs):
- super(EScreen, self).__init__(**kwargs)
- self.old_x = [1, ] * 10
- self._anim = Factory.Animation(speed=0, d=.22)
- from kivy.uix.effectwidget import AdvancedEffectBase
- self.speed = 0
- self.scrollflex = AdvancedEffectBase(
- glsl=self.effect_flex_scroll,
- uniforms={'speed': self.speed}
- )
- self._trigger_straighten = Clock.create_trigger(
- self.straighten_screen, .15)
-
- def on_speed(self, *args):
- value = max(-0.05, min(0.05, float("{0:.5f}".format(args[1]))))
- self.scrollflex.uniforms['speed'] = value
-
- def on_parent(self, instance, value):
- if value:
- value.bind(x=self.screen_moving)
-
- def screen_moving(self, instance, value):
- self.old_x.append(value/self.width)
- self.old_x.pop(0)
- self.speed = sum(((self.old_x[x + 1] - self.old_x[x]) for x in range(9))) / 9.
- self._anim.cancel_all(self)
- self._trigger_straighten()
-
- def straighten_screen(self, dt):
- self._anim.start(self)
-
-
-class ScreenDashboard(EScreen):
+class ScreenDashboard(CScreen):
''' Dashboard screen: Used to display the main dashboard.
'''
@@ -139,7 +71,7 @@ class ScreenAddress(CScreen):
'''
labels = DictProperty({})
- '''
+ ''' A precached list of address labels.
'''
tab = ObjectProperty(None)
@@ -167,15 +99,21 @@ class MainScreen(Factory.Screen):
pass
-class ScreenSend(EScreen):
- pass
+class ScreenSend(CScreen):
+
+ def scan_qr(self):
+ pop = Factory.QrScannerDialog(on_complete=self.set_qr_data)
+ pop.open()
+
+ def set_qr_data(self, uri):
+ self.ids.payto_e
-class ScreenReceive(EScreen):
+class ScreenReceive(CScreen):
pass
-class ScreenContacts(EScreen):
+class ScreenContacts(CScreen):
def add_new_contact(self):
dlg = Cache.get('electrum_widgets', 'NewContactDialog')
diff --git a/gui/kivy/uix/ui_screens/mainscreen.kv b/gui/kivy/uix/ui_screens/mainscreen.kv
@@ -1,6 +1,7 @@
#:import _ electrum.i18n._
#:import Cache kivy.cache.Cache
#:import Factory kivy.factory.Factory
+#:import Decimal decimal.Decimal
#:set font_light 'data/fonts/Roboto-Condensed.ttf'
#:set btc_symbol unichr(171)
#:set mbtc_symbol unichr(187)
@@ -142,15 +143,248 @@
icon='atlas://gui/kivy/theming/light/star_big_inactive',\
duration=1, arrow_pos='', width='250dp')
+
+<TextInputSendBlue@TextInput>
+ padding: '5dp'
+ size_hint: 1, None
+ height: '27dp'
+ pos_hint: {'center_y':.5}
+ multiline: False
+ hint_text_color: self.foreground_color
+ foreground_color: .843, .914, .972, 1
+ background_color: 1, 1, 1, 1
+ background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
+ background_active: 'atlas://gui/kivy/theming/light/textinput_active'
+
+
+<TransactionFeeDialog@SelectionDialog>
+ return_obj: None
+ min_fee: app.format_amount(app.wallet.fee)
+ title:
+ '[size=9dp] \n[/size]Transaction Fee[size=9dp]\n'\
+ '[color=#ADAEAE]Minimum is BTC {}[/color][/size]'.format(self.min_fee)
+ title_size: '24sp'
+ on_activate:
+ ti_fee.focus = True
+ if self.return_obj:\
+ ti_fee.text = "BTC " + self.return_obj.amt
+ on_deactivate: ti_fee.focus = False
+ on_release:
+ if self.return_obj and ti_fee.text:\
+ txt = ti_fee.text;\
+ spc = txt.rfind(' ') + 1;\
+ txt = '' if spc == 0 else txt[spc:];\
+ num = 0 if not txt else float(txt);\
+ self.return_obj.amt = max(self.min_fee, txt)
+ root.dismiss()
+ ELTextInput
+ id: ti_fee
+ size_hint: 1, None
+ height: '34dp'
+ multiline: False
+ on_text_validate: root.dispatch('on_release', self)
+ pos_hint: {'center_y': .7}
+ text: "BTC " + root.min_fee
+ input_type: 'number'
+
+
<ScreenSend>
+ mode: 'address'
name: 'send'
action_view: Factory.SendActionView()
- on_activate:
- root.load_screen('screensend')
on_deactivate:
- self.content.ids.amount_e.focus = False
- self.content.ids.payto_e.focus = False
- self.content.ids.message_e.focus = False
+ self.ids.amount_e.focus = False
+ self.ids.payto_e.focus = False
+ self.ids.message_e.focus = False
+ BoxLayout
+ padding: '12dp', '12dp', '12dp', '12dp'
+ spacing: '12dp'
+ orientation: 'vertical'
+ mode: 'address'
+ SendReceiveToggle:
+ SendToggle:
+ id: toggle_address
+ text: 'ADDRESS'
+ group: 'send_type'
+ state: 'down' if root.mode == 'address' else 'normal'
+ source: 'atlas://gui/kivy/theming/light/globe'
+ background_down: 'atlas://gui/kivy/theming/light/btn_send_address'
+ on_release:
+ if root.mode == 'address': root.mode = 'fc'
+ root.mode = 'address'
+ SendToggle:
+ id: toggle_nfc
+ text: 'NFC'
+ group: 'send_type'
+ state: 'down' if root.mode == 'nfc' else 'normal'
+ source: 'atlas://gui/kivy/theming/light/nfc'
+ background_down: 'atlas://gui/kivy/theming/light/btn_send_nfc'
+ on_release:
+ if root.mode == 'nfc': root.mode = 'str'
+ root.mode = 'nfc'
+ GridLayout:
+ id: grid
+ cols: 1
+ size_hint: 1, None
+ height: self.minimum_height
+ SendReceiveCardTop
+ id: card_address
+ BoxLayout
+ size_hint: 1, None
+ height: '42dp'
+ rows: 1
+ Label
+ id: lbl_symbl
+ bold: True
+ color: amount_e.foreground_color
+ text_size: self.size
+ valign: 'bottom'
+ halign: 'left'
+ font_size: '22sp'
+ text:
+ u'[font={fnt}]{smbl}[/font]'.\
+ format(smbl=btc_symbol if app.base_unit == 'BTC' else mbtc_symbol, fnt=font_light)
+ size_hint_x: .25
+ ELTextInput:
+ id: amount_e
+ input_type: 'number'
+ multiline: False
+ bold: True
+ font_size: '50sp'
+ foreground_color: .308, .308, .308, 1
+ background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
+ pos_hint: {'top': 1.5}
+ size_hint: .7, None
+ height: '67dp'
+ hint_text: 'Amount'
+ text: '0.0'
+ on_text_validate: payto_e.focus = True
+ CardSeparator
+ BoxLayout:
+ size_hint: 1, None
+ height: '42dp'
+ spacing: '5dp'
+ Label:
+ id: fee_e
+ color: .761, .761, .761, 1
+ font_size: '12dp'
+ amt: app.format_amount(app.wallet.fee) if app.wallet else 0
+ text:
+ u'[b]{sign}{symbl}{amt}[/b] of fee'.\
+ format(symbl=lbl_symbl.text,\
+ sign='+' if self.amt > 0 else '-', amt=self.amt)
+ size_hint_x: None
+ width: self.texture_size[0]
+ halign: 'left'
+ valign: 'middle'
+ IconButton:
+ color: 0.694, 0.694, 0.694, 1
+ source: 'atlas://gui/kivy/theming/light/gear'
+ pos_hint: {'center_y': .5}
+ size_hint: None, None
+ size: '22dp', '22dp'
+ on_release:
+ dlg = Cache.get('electrum_widgets', 'TransactionFeeDialog')
+
+ if not dlg:\
+ Factory.register('SelectionDialog', module='electrum_gui.kivy.uix.dialogs');\
+ dlg = Factory.TransactionFeeDialog();\
+ Cache.append('electrum_widgets', 'TransactionDialog', dlg)
+
+ dlg.return_obj = fee_e
+ dlg.open()
+ Label:
+ font_size: '12dp'
+ color: fee_e.color
+ text: u'= {}'.format(app.create_quote_text(Decimal(float(amount_e.text)), mode='symbol')) if amount_e.text else u'0'
+ text_size: self.size
+ halign: 'right'
+ valign: 'middle'
+ SendReceiveBlueBottom:
+ id: blue_bottom
+ size_hint: 1, None
+ height: self.minimum_height
+ BoxLayout
+ size_hint: 1, None
+ height: blue_bottom.item_height
+ spacing: '5dp'
+ Image:
+ source: 'atlas://gui/kivy/theming/light/contact'
+ size_hint: None, None
+ size: '22dp', '22dp'
+ pos_hint: {'center_y': .5}
+ TextInputSendBlue:
+ id: payto_e
+ hint_text: "Enter Contact or adress"
+ on_text_validate:
+ Factory.Animation(opacity=1,\
+ height=blue_bottom.item_height)\
+ .start(message_selection)
+ message_e.focus = True
+ Widget:
+ size_hint: None, None
+ width: dp(2)
+ height: qr.height
+ pos_hint: {'center_y':.5}
+ canvas.after:
+ Rectangle:
+ size: self.size
+ pos: self.pos
+ IconButton:
+ id: qr
+ source: 'atlas://gui/kivy/theming/light/qrcode'
+ pos_hint: {'center_y': .5}
+ size_hint: None, None
+ size: '22dp', '22dp'
+ on_release: root.scan_qr()
+ CardSeparator
+ opacity: message_selection.opacity
+ color: blue_bottom.foreground_color
+ BoxLayout:
+ id: message_selection
+ opacity: 1 if app.expert_mode else 0
+ size_hint: 1, None
+ height: blue_bottom.item_height if app.expert_mode else 0
+ spacing: '5dp'
+ Image:
+ source: 'atlas://gui/kivy/theming/light/pen'
+ size_hint: None, None
+ size: '22dp', '22dp'
+ pos_hint: {'center_y': .5}
+ TextInputSendBlue:
+ id: message_e
+ hint_text: 'Enter description here'
+ on_text_validate:
+ anim = Factory.Animation(opacity=1, height=blue_bottom.item_height)
+ anim.start(wallet_selection)
+ #anim.start(address_selection)
+ CardSeparator
+ opacity: wallet_selection.opacity
+ color: blue_bottom.foreground_color
+ WalletSelector:
+ id: wallet_selection
+ foreground_color: blue_bottom.foreground_color
+ opacity: 1 if app.expert_mode else 0
+ size_hint: 1, None
+ height: blue_bottom.item_height if app.expert_mode else 0
+ CardSeparator
+ opacity: address_selection.opacity
+ color: blue_bottom.foreground_color
+ AddressSelector:
+ id: address_selection
+ foreground_color: blue_bottom.foreground_color
+ opacity: 1 if app.expert_mode else 0
+ size_hint: 1, None
+ height: blue_bottom.item_height if app.expert_mode else 0
+ CreateAccountButtonGreen:
+ background_color: (1, 1, 1, 1) if self.disabled else ((.258, .80, .388, 1) if self.state == 'normal' else (.203, .490, .741, 1))
+ text: _('Goto next step') if app.wallet and app.wallet.seed else _('Create unsigned transaction')
+ size_hint_y: None
+ height: '38dp'
+ disabled: True if wallet_selection.opacity == 0 else False
+ on_release: app.do_send()
+ Widget
+
<SendToggle@ToggleButton>
source: ''
@@ -200,7 +434,7 @@
<AddressSelector@BlueSpinner>
icon: 'atlas://gui/kivy/theming/light/globe'
- values: app.wallet.addresses()
+ values: app.wallet.addresses() if app.wallet else []
text: _("Select Your address")
<WalletSelector@BlueSpinner>
@@ -253,12 +487,146 @@
<ScreenReceive>
+ mode: 'qr'
name: 'receive'
action_view: Factory.ReceiveActionView()
on_activate:
- root.load_screen('screenreceive')
+ self.ids.toggle_qr.state = 'down'
+ first_address = app.wallet.addresses()[0]
+ qr.data = app.encode_uri(first_address,
+ amount=amount_e.text,
+ label=app.wallet.labels.get(first_address, first_address),
+ message='') if app.wallet and app.wallet.addresses() else ''
on_deactivate:
- self.content.ids.amount_e.focus = False
+ self.ids.amount_e.focus = False
+ BoxLayout
+ padding: '12dp', '12dp', '12dp', '12dp'
+ spacing: '12dp'
+ mode: 'qr'
+ orientation: 'vertical'
+ SendReceiveToggle
+ SendToggle:
+ id: toggle_qr
+ text: 'QR'
+ state: 'down' if root.mode == 'qr' else 'normal'
+ source: 'atlas://gui/kivy/theming/light/qrcode'
+ background_down: 'atlas://gui/kivy/theming/light/btn_send_address'
+ on_release:
+ if root.mode == 'qr': root.mode = 'nr'
+ root.mode = 'qr'
+ SendToggle:
+ id: toggle_nfc
+ text: 'NFC'
+ state: 'down' if root.mode == 'nfc' else 'normal'
+ source: 'atlas://gui/kivy/theming/light/nfc'
+ background_down: 'atlas://gui/kivy/theming/light/btn_send_nfc'
+ on_release:
+ if root.mode == 'nfc': root.mode = 'nr'
+ root.mode = 'nfc'
+ GridLayout:
+ id: grid
+ cols: 1
+ #size_hint: 1, None
+ #height: self.minimum_height
+ SendReceiveCardTop
+ height: '110dp'
+ BoxLayout:
+ size_hint: 1, None
+ height: '42dp'
+ rows: 1
+ Label:
+ color: amount_e.foreground_color
+ bold: True
+ text_size: self.size
+ valign: 'bottom'
+ font_size: '22sp'
+ text:
+ u'[font={fnt}]{smbl}[/font]'.\
+ format(smbl=btc_symbol if app.base_unit == 'BTC' else mbtc_symbol, fnt=font_light)
+ size_hint_x: .25
+ ELTextInput:
+ id: amount_e
+ input_type: 'number'
+ multiline: False
+ bold: True
+ font_size: '50sp'
+ foreground_color: .308, .308, .308, 1
+ background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
+ pos_hint: {'top': 1.5}
+ size_hint: .7, None
+ height: '67dp'
+ hint_text: 'Amount'
+ text: '0.0'
+ CardSeparator
+ BoxLayout:
+ size_hint: 1, None
+ height: '32dp'
+ spacing: '5dp'
+ Label:
+ color: lbl_quote.color
+ font_size: '12dp'
+ text: 'Ask to scan the QR below'
+ text_size: self.size
+ halign: 'left'
+ valign: 'middle'
+ Label:
+ id: lbl_quote
+ font_size: '12dp'
+ size_hint: .5, 1
+ color: .761, .761, .761, 1
+ text: u'= {}'.format(app.create_quote_text(Decimal(float(amount_e.text)), mode='symbol')) if amount_e.text else u'0'
+ text_size: self.size
+ halign: 'right'
+ valign: 'middle'
+ SendReceiveBlueBottom
+ id: blue_bottom
+ padding: '12dp', 0, '12dp', '12dp'
+ WalletSelector:
+ id: wallet_selection
+ foreground_color: blue_bottom.foreground_color
+ size_hint: 1, None
+ height: blue_bottom.item_height
+ CardSeparator
+ opacity: wallet_selection.opacity
+ color: blue_bottom.foreground_color
+ AddressSelector:
+ id: address_selection
+ foreground_color: blue_bottom.foreground_color
+ opacity: 1 if app.expert_mode else 0
+ size_hint: 1, None
+ height: blue_bottom.item_height if app.expert_mode else 0
+ on_text:
+ if not args[1].startswith('Select'):\
+ qr.data = app.encode_uri(args[1],\
+ amount=amount_e.text,\
+ label=app.wallet.labels.get(args[1], args[1]),\
+ message='')
+ CardSeparator
+ opacity: address_selection.opacity
+ color: blue_bottom.foreground_color
+ Widget:
+ size_hint_y: None
+ height: dp(10)
+ FloatLayout
+ id: bl
+ QRCodeWidget:
+ id: qr
+ size_hint: None, 1
+ width: min(self.height, bl.width)
+ pos_hint: {'center': (.5, .5)}
+ on_touch_down:
+ if self.collide_point(*args[1].pos):\
+ app.show_info_bubble(icon=self.ids.qrimage.texture, text='texture')
+ CreateAccountButtonGreen:
+ background_color: (1, 1, 1, 1) if self.disabled else ((.258, .80, .388, 1) if self.state == 'normal' else (.203, .490, .741, 1))
+ text: _('Goto next step') if app.wallet and app.wallet.seed else _('Create unsigned transaction')
+ size_hint_y: None
+ height: '38dp'
+ disabled: True if wallet_selection.opacity == 0 else False
+ on_release:
+ message = 'sending {} {} to {}'.format(\
+ app.base_unit, amount_e.text, payto_e.text)
+ app.gui.main_gui.do_send(self, message=message)
<ReceiveActionView@ActionView>
WalletActionPrevious:
@@ -350,12 +718,12 @@
pos_hint: {'center_x': .5, 'y': .02}
allow_stretch: True
size: ('32dp', '32dp')
- Label:
- id: screen_label
- size: root.size
- font_size: '45sp'
- color: root.color
- text: '' if root.state == 'activated' or root.content else 'Loading...'
+ #Label:
+ #id: screen_label
+ #size: root.size
+ #font_size: '45sp'
+ #color: root.color
+ #text: '' if root.state == 'activated' or root.content else 'Loading...'
<EScreen>
background_color: .929, .929, .929 ,1
@@ -1361,7 +1729,7 @@
# name: 'password
<Drawer>
- overlay_widget: overlay_widget
+ #overlay_widget: overlay_widget
RelativeLayout:
id: hidden_widget
size_hint: None, None
diff --git a/gui/kivy/uix/ui_screens/screenreceive.kv b/gui/kivy/uix/ui_screens/screenreceive.kv
@@ -1,138 +0,0 @@
-#:import Decimal decimal.Decimal
-
-<ScreenReceiveContent@BoxLayout>
- opacity: 0
- padding: '12dp', '12dp', '12dp', '12dp'
- spacing: '12dp'
- mode: 'qr'
- orientation: 'vertical'
- on_parent:
- if args[1]:\
- first_address = app.wallet.addresses()[0];\
- qr.data = app.encode_uri(first_address,\
- amount=amount_e.text,\
- label=app.wallet.labels.get(first_address, first_address),\
- message='') if app.wallet.addresses() else ''
- SendReceiveToggle
- SendToggle:
- id: toggle_qr
- text: 'QR'
- state: 'down' if root.mode == 'qr' else 'normal'
- source: 'atlas://gui/kivy/theming/light/qrcode'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_address'
- on_release:
- if root.mode == 'qr': root.mode = 'nr'
- root.mode = 'qr'
- SendToggle:
- id: toggle_nfc
- text: 'NFC'
- state: 'down' if root.mode == 'nfc' else 'normal'
- source: 'atlas://gui/kivy/theming/light/nfc'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_nfc'
- on_release:
- if root.mode == 'nfc': root.mode = 'nr'
- root.mode = 'nfc'
- GridLayout:
- id: grid
- cols: 1
- #size_hint: 1, None
- #height: self.minimum_height
- SendReceiveCardTop
- height: '110dp'
- BoxLayout:
- size_hint: 1, None
- height: '42dp'
- rows: 1
- Label:
- color: amount_e.foreground_color
- bold: True
- text_size: self.size
- valign: 'bottom'
- font_size: '22sp'
- text:
- u'[font={fnt}]{smbl}[/font]'.\
- format(smbl=btc_symbol if app.base_unit == 'BTC' else mbtc_symbol, fnt=font_light)
- size_hint_x: .25
- ELTextInput:
- id: amount_e
- input_type: 'number'
- multiline: False
- bold: True
- font_size: '50sp'
- foreground_color: .308, .308, .308, 1
- background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
- pos_hint: {'top': 1.5}
- size_hint: .7, None
- height: '67dp'
- hint_text: 'Amount'
- text: '0.0'
- CardSeparator
- BoxLayout:
- size_hint: 1, None
- height: '32dp'
- spacing: '5dp'
- Label:
- color: lbl_quote.color
- font_size: '12dp'
- text: 'Ask to scan the QR below'
- text_size: self.size
- halign: 'left'
- valign: 'middle'
- Label:
- id: lbl_quote
- font_size: '12dp'
- size_hint: .5, 1
- color: .761, .761, .761, 1
- text: u'= {}'.format(app.create_quote_text(Decimal(float(amount_e.text)), mode='symbol')) if amount_e.text else u'0'
- text_size: self.size
- halign: 'right'
- valign: 'middle'
- SendReceiveBlueBottom
- id: blue_bottom
- padding: '12dp', 0, '12dp', '12dp'
- WalletSelector:
- id: wallet_selection
- foreground_color: blue_bottom.foreground_color
- size_hint: 1, None
- height: blue_bottom.item_height
- CardSeparator
- opacity: wallet_selection.opacity
- color: blue_bottom.foreground_color
- AddressSelector:
- id: address_selection
- foreground_color: blue_bottom.foreground_color
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- on_text:
- if not args[1].startswith('Select'):\
- qr.data = app.encode_uri(args[1],\
- amount=amount_e.text,\
- label=app.wallet.labels.get(args[1], args[1]),\
- message='')
- CardSeparator
- opacity: address_selection.opacity
- color: blue_bottom.foreground_color
- Widget:
- size_hint_y: None
- height: dp(10)
- FloatLayout
- id: bl
- QRCodeWidget:
- id: qr
- size_hint: None, 1
- width: min(self.height, bl.width)
- pos_hint: {'center': (.5, .5)}
- on_touch_down:
- if self.collide_point(*args[1].pos):\
- app.show_info_bubble(icon=self.ids.qrimage.texture, text='texture')
- CreateAccountButtonGreen:
- background_color: (1, 1, 1, 1) if self.disabled else ((.258, .80, .388, 1) if self.state == 'normal' else (.203, .490, .741, 1))
- text: _('Goto next step') if app.wallet.seed else _('Create unsigned transaction')
- size_hint_y: None
- height: '38dp'
- disabled: True if wallet_selection.opacity == 0 else False
- on_release:
- message = 'sending {} {} to {}'.format(\
- app.base_unit, amount_e.text, payto_e.text)
- app.gui.main_gui.do_send(self, message=message)-
\ No newline at end of file
diff --git a/gui/kivy/uix/ui_screens/screensend.kv b/gui/kivy/uix/ui_screens/screensend.kv
@@ -1,232 +0,0 @@
-#:import Decimal decimal.Decimal
-
-<TextInputSendBlue@TextInput>
- padding: '5dp'
- size_hint: 1, None
- height: '27dp'
- pos_hint: {'center_y':.5}
- multiline: False
- hint_text_color: self.foreground_color
- foreground_color: .843, .914, .972, 1
- background_color: 1, 1, 1, 1
- background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
- background_active: 'atlas://gui/kivy/theming/light/textinput_active'
-
-<TransactionFeeDialog@SelectionDialog>
- return_obj: None
- min_fee: app.format_amount(app.wallet.fee)
- title:
- '[size=9dp] \n[/size]Transaction Fee[size=9dp]\n'\
- '[color=#ADAEAE]Minimum is BTC {}[/color][/size]'.format(self.min_fee)
- title_size: '24sp'
- on_activate:
- ti_fee.focus = True
- if self.return_obj:\
- ti_fee.text = "BTC " + self.return_obj.amt
- on_deactivate: ti_fee.focus = False
- on_release:
- if self.return_obj and ti_fee.text:\
- txt = ti_fee.text;\
- spc = txt.rfind(' ') + 1;\
- txt = '' if spc == 0 else txt[spc:];\
- num = 0 if not txt else float(txt);\
- self.return_obj.amt = max(self.min_fee, txt)
- root.dismiss()
- ELTextInput
- id: ti_fee
- size_hint: 1, None
- height: '34dp'
- multiline: False
- on_text_validate: root.dispatch('on_release', self)
- pos_hint: {'center_y': .7}
- text: "BTC " + root.min_fee
- input_type: 'number'
-
-<ScreenSendContent@BoxLayout>
- opacity: 0
- padding: '12dp', '12dp', '12dp', '12dp'
- spacing: '12dp'
- orientation: 'vertical'
- mode: 'address'
- SendReceiveToggle:
- SendToggle:
- id: toggle_address
- text: 'ADDRESS'
- group: 'send_type'
- state: 'down' if root.mode == 'address' else 'normal'
- source: 'atlas://gui/kivy/theming/light/globe'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_address'
- on_release:
- if root.mode == 'address': root.mode = 'fc'
- root.mode = 'address'
- SendToggle:
- id: toggle_nfc
- text: 'NFC'
- group: 'send_type'
- state: 'down' if root.mode == 'nfc' else 'normal'
- source: 'atlas://gui/kivy/theming/light/nfc'
- background_down: 'atlas://gui/kivy/theming/light/btn_send_nfc'
- on_release:
- if root.mode == 'nfc': root.mode = 'str'
- root.mode = 'nfc'
- GridLayout:
- id: grid
- cols: 1
- size_hint: 1, None
- height: self.minimum_height
- SendReceiveCardTop
- id: card_address
- BoxLayout
- size_hint: 1, None
- height: '42dp'
- rows: 1
- Label
- id: lbl_symbl
- bold: True
- color: amount_e.foreground_color
- text_size: self.size
- valign: 'bottom'
- halign: 'left'
- font_size: '22sp'
- text:
- u'[font={fnt}]{smbl}[/font]'.\
- format(smbl=btc_symbol if app.base_unit == 'BTC' else mbtc_symbol, fnt=font_light)
- size_hint_x: .25
- ELTextInput:
- id: amount_e
- input_type: 'number'
- multiline: False
- bold: True
- font_size: '50sp'
- foreground_color: .308, .308, .308, 1
- background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
- pos_hint: {'top': 1.5}
- size_hint: .7, None
- height: '67dp'
- hint_text: 'Amount'
- text: '0.0'
- on_text_validate: payto_e.focus = True
- CardSeparator
- BoxLayout:
- size_hint: 1, None
- height: '42dp'
- spacing: '5dp'
- Label:
- id: fee_e
- color: .761, .761, .761, 1
- font_size: '12dp'
- amt: app.format_amount(app.wallet.fee)
- text:
- u'[b]{sign}{symbl}{amt}[/b] of fee'.\
- format(symbl=lbl_symbl.text,\
- sign='+' if self.amt > 0 else '-', amt=self.amt)
- size_hint_x: None
- width: self.texture_size[0]
- halign: 'left'
- valign: 'middle'
- IconButton:
- color: 0.694, 0.694, 0.694, 1
- source: 'atlas://gui/kivy/theming/light/gear'
- pos_hint: {'center_y': .5}
- size_hint: None, None
- size: '22dp', '22dp'
- on_release:
- dlg = Cache.get('electrum_widgets', 'TransactionFeeDialog')
-
- if not dlg:\
- Factory.register('SelectionDialog', module='electrum_gui.kivy.uix.dialogs');\
- dlg = Factory.TransactionFeeDialog();\
- Cache.append('electrum_widgets', 'TransactionDialog', dlg)
-
- dlg.return_obj = fee_e
- dlg.open()
- Label:
- font_size: '12dp'
- color: fee_e.color
- text: u'= {}'.format(app.create_quote_text(Decimal(float(amount_e.text)), mode='symbol')) if amount_e.text else u'0'
- text_size: self.size
- halign: 'right'
- valign: 'middle'
- SendReceiveBlueBottom:
- id: blue_bottom
- size_hint: 1, None
- height: self.minimum_height
- BoxLayout
- size_hint: 1, None
- height: blue_bottom.item_height
- spacing: '5dp'
- Image:
- source: 'atlas://gui/kivy/theming/light/contact'
- size_hint: None, None
- size: '22dp', '22dp'
- pos_hint: {'center_y': .5}
- TextInputSendBlue:
- id: payto_e
- hint_text: "Enter Contact or adress"
- on_text_validate:
- Factory.Animation(opacity=1,\
- height=blue_bottom.item_height)\
- .start(message_selection)
- message_e.focus = True
- Widget:
- size_hint: None, None
- width: dp(2)
- height: qr.height
- pos_hint: {'center_y':.5}
- canvas.after:
- Rectangle:
- size: self.size
- pos: self.pos
- IconButton:
- id: qr
- source: 'atlas://gui/kivy/theming/light/qrcode'
- pos_hint: {'center_y': .5}
- size_hint: None, None
- size: '22dp', '22dp'
- CardSeparator
- opacity: message_selection.opacity
- color: blue_bottom.foreground_color
- BoxLayout:
- id: message_selection
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- spacing: '5dp'
- Image:
- source: 'atlas://gui/kivy/theming/light/pen'
- size_hint: None, None
- size: '22dp', '22dp'
- pos_hint: {'center_y': .5}
- TextInputSendBlue:
- id: message_e
- hint_text: 'Enter description here'
- on_text_validate:
- anim = Factory.Animation(opacity=1, height=blue_bottom.item_height)
- anim.start(wallet_selection)
- #anim.start(address_selection)
- CardSeparator
- opacity: wallet_selection.opacity
- color: blue_bottom.foreground_color
- WalletSelector:
- id: wallet_selection
- foreground_color: blue_bottom.foreground_color
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- CardSeparator
- opacity: address_selection.opacity
- color: blue_bottom.foreground_color
- AddressSelector:
- id: address_selection
- foreground_color: blue_bottom.foreground_color
- opacity: 1 if app.expert_mode else 0
- size_hint: 1, None
- height: blue_bottom.item_height if app.expert_mode else 0
- CreateAccountButtonGreen:
- background_color: (1, 1, 1, 1) if self.disabled else ((.258, .80, .388, 1) if self.state == 'normal' else (.203, .490, .741, 1))
- text: _('Goto next step') if app.wallet.seed else _('Create unsigned transaction')
- size_hint_y: None
- height: '38dp'
- disabled: True if wallet_selection.opacity == 0 else False
- on_release: app.do_send()
- Widget