kivy: split mainscreen.kv into dynamically loaded .kv files
This commit is contained in:
parent
d68042e76e
commit
65ecbf990d
451
gui/kivy/main.kv
451
gui/kivy/main.kv
|
@ -1,4 +1,6 @@
|
|||
#:import Window kivy.core.window.Window
|
||||
#:import Factory kivy.factory.Factory
|
||||
#:import _ electrum.i18n._
|
||||
|
||||
# Custom Global Widgets
|
||||
|
||||
|
@ -7,6 +9,79 @@
|
|||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
|
||||
<LightOptions@SpinnerOption>
|
||||
font_size: '14sp'
|
||||
border: 4, 4, 4, 4
|
||||
color: 0.439, 0.439, 0.439, .8
|
||||
background_normal: 'atlas://gui/kivy/theming/light/action_button_group'
|
||||
background_down: 'atlas://gui/kivy/theming/light/overflow_btn_dn'
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
text_size: self.size[0] - dp(20), self.size[1]
|
||||
halign: 'left'
|
||||
valign: 'middle'
|
||||
shorten: True
|
||||
on_press:
|
||||
ddn = self.parent.parent
|
||||
Factory.Animation(opacity=0, d=.25).start(ddn)
|
||||
|
||||
<OppositeDropDown@DropDown>
|
||||
#auto_width: False
|
||||
size_hint: None, None
|
||||
size: self.container.minimum_size if self.container else (0, 0)
|
||||
on_container: if args[1]: self.container.padding = '4dp', '4dp', '4dp', '4dp'
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 1, 1, 1, 1
|
||||
BorderImage:
|
||||
pos:self.pos
|
||||
border: 20, 20, 20, 20
|
||||
source: 'atlas://gui/kivy/theming/light/dropdown_background'
|
||||
size: self.size
|
||||
|
||||
<OppositeSpinner@CSpinner>
|
||||
dropdown_cls: Factory.OppositeDropDown
|
||||
option_cls: Factory.LightOptions
|
||||
border: 20, 20, 9, 9
|
||||
background_normal: 'atlas://gui/kivy/theming/light/action_group_dark'
|
||||
background_down: self.background_normal
|
||||
values: ('Copy to clipboard', 'Send Payment')
|
||||
size_hint: None, 1
|
||||
width: '12dp'
|
||||
on_release:
|
||||
ddn = self._dropdown
|
||||
ddn.opacity = 0
|
||||
Factory.Animation(opacity=1, d=.25).start(ddn)
|
||||
|
||||
|
||||
<BlueSpinner@BoxLayout>
|
||||
foreground_color: 1, 1, 1, 1
|
||||
spacing: '9dp'
|
||||
text: ''
|
||||
values: ('', )
|
||||
icon: ''
|
||||
Image:
|
||||
source: root.icon
|
||||
size_hint: None, None
|
||||
size: '22dp', '22dp'
|
||||
pos_hint: {'center_y': .5}
|
||||
OppositeSpinner:
|
||||
color: root.foreground_color
|
||||
background_normal: 'atlas://gui/kivy/theming/light/action_group_light'
|
||||
markup: False
|
||||
shorten: True
|
||||
font_size: '16dp'
|
||||
size_hint: 1, .7
|
||||
pos_hint: {'center_y': .5}
|
||||
text: root.text
|
||||
text_size: self.size
|
||||
halign: 'left'
|
||||
valign: 'middle'
|
||||
on_text:
|
||||
root.text = args[1]
|
||||
values: root.values
|
||||
|
||||
|
||||
<IconButton@ButtonBehavior+Image>
|
||||
allow_stretch: True
|
||||
size_hint_x: None
|
||||
|
@ -50,39 +125,8 @@
|
|||
background_normal: 'atlas://gui/kivy/theming/light/textinput_active'
|
||||
|
||||
|
||||
<CreateAccountButtonBlue@Button>
|
||||
canvas.after:
|
||||
Color
|
||||
rgba: 1, 1, 1, 1 if self.disabled else 0
|
||||
Rectangle:
|
||||
texture: self.texture
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
Color
|
||||
rgba: .5, .5, .5, .5 if self.disabled else 0
|
||||
Rectangle:
|
||||
texture: self.texture
|
||||
size: self.size
|
||||
pos: self.x - dp(1), self.y + dp(1)
|
||||
border: 15, 5, 5, 5
|
||||
background_color: (1, 1, 1, 1) if self.disabled else (.203, .490, .741, 1 if self.state == 'normal' else .75)
|
||||
size_hint: 1, None
|
||||
height: '48sp'
|
||||
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_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
|
||||
# Global Defaults
|
||||
###########################
|
||||
<TextInput>
|
||||
on_focus: app._focused_widget = root
|
||||
|
@ -127,11 +171,346 @@
|
|||
size_hint: 1, 1
|
||||
width: 0 if root.fs else (root.width - img.width)
|
||||
|
||||
StencilView:
|
||||
manager: None
|
||||
|
||||
|
||||
<WalletActionPrevious@ActionPrevious>
|
||||
app_icon: 'atlas://gui/kivy/theming/light/' + ('wallets' if app.ui_mode[0] != 't' else 'tab_btn')
|
||||
with_previous: False
|
||||
size_hint: None, 1
|
||||
mipmap: True
|
||||
on_release: app.root.children[0].toggle_drawer()
|
||||
|
||||
|
||||
<SendReceiveToggle@BoxLayout>
|
||||
padding: '5dp', '5dp'
|
||||
size_hint: 1, None
|
||||
height: '45dp'
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 1, 1, 1, 1
|
||||
Rectangle
|
||||
BorderImage:
|
||||
border: 12, 12, 12, 12
|
||||
source: 'atlas://gui/kivy/theming/light/card'
|
||||
size: self.width + dp(3), self.height
|
||||
pos: self.x - dp(1.5), self.y
|
||||
|
||||
<SendReceiveCardTop@GridLayout>
|
||||
canvas.before:
|
||||
BorderImage:
|
||||
border: 9, 9, 9, 9
|
||||
source: 'atlas://gui/kivy/theming/light/card_top'
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
pos:self.pos
|
||||
padding: '12dp', '22dp', '12dp', 0
|
||||
cols: 1
|
||||
size_hint: 1, None
|
||||
height: '120dp'
|
||||
spacing: '4dp'
|
||||
|
||||
<SendReceiveBlueBottom@GridLayout>
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: .238, .585, .878, 1
|
||||
BorderImage:
|
||||
border: 9, 9, 9, 9
|
||||
source: 'atlas://gui/kivy/theming/light/card_bottom'
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
Color:
|
||||
rgba: 1, 1, 1, 1
|
||||
|
||||
item_height: dp(42)
|
||||
foreground_color: .843, .914, .972, 1
|
||||
cols: 1
|
||||
padding: '12dp', 0
|
||||
|
||||
|
||||
<SendToggle@ToggleButton>
|
||||
source: ''
|
||||
group: 'transfer_type'
|
||||
markup: False
|
||||
bold: True
|
||||
border: 4, 4, 4, 4
|
||||
background_normal: self.background_down
|
||||
color:
|
||||
(.140, .140, .140, 1) if self.state == 'down' else (.796, .796, .796, 1)
|
||||
canvas.after:
|
||||
Color:
|
||||
rgba: 1, 1, 1, 1
|
||||
Image:
|
||||
source: root.source
|
||||
color: root.color
|
||||
size: '30dp', '30dp'
|
||||
center_x: root.center_x - ((root.texture_size[0]/2)+(self.width/1.5))
|
||||
center_y: root.center_y
|
||||
|
||||
|
||||
<CardSeparator@Widget>
|
||||
size_hint: 1, None
|
||||
height: dp(1)
|
||||
color: .909, .909, .909, 1
|
||||
canvas:
|
||||
Color:
|
||||
rgba: root.color if root.color else (0, 0, 0, 0)
|
||||
Rectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
|
||||
<AddressSelector@BlueSpinner>
|
||||
icon: 'atlas://gui/kivy/theming/light/globe'
|
||||
values: app.wallet.addresses() if app.wallet else []
|
||||
text: _("Select Your address")
|
||||
|
||||
|
||||
<ElectrumScreen>
|
||||
ScrollView:
|
||||
do_scroll_x: False
|
||||
do_scroll_y: False if root.fullscreen else (content.height > root.height - dp(16))
|
||||
AnchorLayout:
|
||||
size_hint_y: None
|
||||
height: root.height if root.fullscreen else max(root.height, content.height)
|
||||
GridLayout:
|
||||
id: content
|
||||
cols: 1
|
||||
spacing: '8dp'
|
||||
padding: '8dp'
|
||||
size_hint: (1, 1) if root.fullscreen else (.8, None)
|
||||
height: self.height if root.fullscreen else self.minimum_height
|
||||
|
||||
|
||||
<TabbedCarousel>
|
||||
carousel: carousel
|
||||
do_default_tab: False
|
||||
Carousel:
|
||||
scroll_timeout: 190
|
||||
anim_type: 'out_quart'
|
||||
min_move: .05
|
||||
anim_move_duration: .1
|
||||
anim_cancel_duration: .54
|
||||
scroll_distance: '10dp'
|
||||
on_index: root.on_index(*args)
|
||||
id: carousel
|
||||
|
||||
|
||||
<CarouselIndicator@TabbedCarousel>
|
||||
tab_pos: 'bottom_mid'
|
||||
tab_height: '32dp'
|
||||
tab_width: self.tab_height
|
||||
#background_image: 'atlas://data/images/defaulttheme/action_item'
|
||||
strip_border: 0, 0, 0, 0
|
||||
|
||||
<CloseButton@IconButton>
|
||||
source: 'atlas://gui/kivy/theming/light/closebutton'
|
||||
opacity: 1 if self.state == 'normal' else .75
|
||||
size_hint: None, None
|
||||
size: '27dp', '27dp'
|
||||
|
||||
<-CarouselDialog>
|
||||
header_color: '#707070ff'
|
||||
text_color: 0.701, 0.701, 0.701, 1
|
||||
title_size: '13sp'
|
||||
title: ''
|
||||
separator_color: 0.89, 0.89, 0.89, 1
|
||||
background: 'atlas://gui/kivy/theming/light/tab_btn'
|
||||
carousel_content: carousel_content
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0, 0, 0, .9
|
||||
Rectangle:
|
||||
size: Window.size
|
||||
pos: 0, 0
|
||||
Color:
|
||||
rgba: 1, 1, 1, 1
|
||||
BorderImage:
|
||||
border: 12, 12, 12, 12
|
||||
source: 'atlas://gui/kivy/theming/light/dialog'
|
||||
size: root.width, root.height - self.carousel_content.tab_height if self.carousel_content else 0
|
||||
pos: root.x, self.y + self.carousel_content.tab_height if self.carousel_content else 10
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
GridLayout:
|
||||
cols: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
padding: 0, '7sp'
|
||||
Label:
|
||||
font_size: root.title_size
|
||||
text: u'[color={}]{}[/color]'.format(root.header_color, root.title)
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
size_hint: 1, None
|
||||
height: self.texture_size[1]
|
||||
CardSeparator:
|
||||
color: root.separator_color
|
||||
height: root.separator_height
|
||||
FloatLayout:
|
||||
size_hint: None, None
|
||||
size: 0, 0
|
||||
CloseButton:
|
||||
id: but_close
|
||||
top: root.top - dp(10)
|
||||
right: root.right - dp(10)
|
||||
on_release: root.dismiss()
|
||||
CarouselIndicator:
|
||||
id: carousel_content
|
||||
|
||||
|
||||
<CleanHeader@TabbedPanelHeader>
|
||||
border: 0, 0, 16, 0
|
||||
markup: False
|
||||
text_size: self.size
|
||||
halign: 'center'
|
||||
valign: 'middle'
|
||||
bold: True
|
||||
font_size: '12.5sp'
|
||||
background_normal: 'atlas://gui/kivy/theming/light/tab_btn'
|
||||
background_disabled_normal: 'atlas://gui/kivy/theming/light/tab_btn_disabled'
|
||||
background_down: 'atlas://gui/kivy/theming/light/tab_btn_pressed'
|
||||
#canvas.before:
|
||||
# Color:
|
||||
# rgba: .6, .6, .6, .7
|
||||
# Rectangle:
|
||||
# size: self.size
|
||||
# pos: self.x + 1, self.y - 1
|
||||
# texture: self.texture
|
||||
|
||||
|
||||
<ColoredLabel@Label>:
|
||||
font_size: '48sp'
|
||||
color: (.6, .6, .6, 1)
|
||||
canvas.before:
|
||||
Color:
|
||||
rgb: (.9, .9, .9)
|
||||
Rectangle:
|
||||
pos: self.x + sp(2), self.y + sp(2)
|
||||
size: self.width - sp(4), self.height - sp(4)
|
||||
|
||||
|
||||
|
||||
<ScreenTabs@Screen>
|
||||
TabbedCarousel:
|
||||
id: panel
|
||||
tab_height: '48dp'
|
||||
default_tab: send_tab
|
||||
strip_border: 0, 0, 0, 0
|
||||
|
||||
HistoryScreen:
|
||||
id: history_screen
|
||||
tab: history_tab
|
||||
|
||||
SendScreen:
|
||||
id: send_screen
|
||||
tab: send_tab
|
||||
|
||||
ReceiveScreen:
|
||||
id: receive_screen
|
||||
tab: receive_tab
|
||||
|
||||
ContactsScreen:
|
||||
id: contacts_screen
|
||||
tab: contacts_tab
|
||||
|
||||
CleanHeader:
|
||||
id: history_tab
|
||||
text: _('History')
|
||||
slide: 0
|
||||
CleanHeader:
|
||||
id: send_tab
|
||||
text: _('Send')
|
||||
slide: 1
|
||||
CleanHeader:
|
||||
id: receive_tab
|
||||
text: _('Receive')
|
||||
slide: 2
|
||||
CleanHeader:
|
||||
id: contacts_tab
|
||||
text: _('Contacts')
|
||||
slide: 3
|
||||
|
||||
|
||||
<ActionButton>:
|
||||
border: 4, 0, 0, 0
|
||||
#background_down: 'atlas://gui/kivy/theming/light/overflow_btn_dn'
|
||||
|
||||
<OverflowButton@ActionButton>
|
||||
text_size: dp(50), None
|
||||
last: False
|
||||
halign: 'left'
|
||||
valign: 'middle'
|
||||
overflow: None
|
||||
#background_normal:
|
||||
# 'atlas://gui/kivy/theming/light/' +\
|
||||
# ('action_button_group'\
|
||||
# if (self.inside_group and not self.last) else 'tab_btn')
|
||||
|
||||
#on_press:
|
||||
# ddn = self.overflow._dropdown
|
||||
# Factory.Animation.cancel_all(ddn)
|
||||
# anim = Factory.Animation(opacity=0, d=.25)
|
||||
# anim.bind(on_complete=ddn.dismiss)
|
||||
# anim.start(ddn)
|
||||
|
||||
BoxLayout:
|
||||
|
||||
orientation: 'vertical'
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgb: .6, .6, .6
|
||||
Rectangle:
|
||||
size: self.size
|
||||
source: 'gui/kivy/data/background.png'
|
||||
|
||||
ActionBar:
|
||||
|
||||
ActionView:
|
||||
id: av
|
||||
|
||||
ActionPrevious:
|
||||
app_icon: 'atlas://gui/kivy/theming/light/logo'
|
||||
with_previous: False
|
||||
on_release: app.on_back()
|
||||
|
||||
ActionButton:
|
||||
id: action_status
|
||||
important: True
|
||||
size_hint: 1, 1
|
||||
markup: True
|
||||
mipmap: True
|
||||
bold: True
|
||||
markup: True
|
||||
color: 1, 1, 1, 1
|
||||
text:
|
||||
"[color=#777777]{}[/color]"\
|
||||
.format(app.status)
|
||||
font_size: '22dp'
|
||||
minimum_width: '1dp'
|
||||
|
||||
ActionOverflow:
|
||||
id: action_overflow
|
||||
width: '60dp'
|
||||
OverflowButton:
|
||||
text: _('Network')
|
||||
overflow: action_overflow
|
||||
on_release: app.popup_dialog('network')
|
||||
OverflowButton:
|
||||
text: _('Wallet')
|
||||
overflow: action_overflow
|
||||
on_release: app.popup_dialog('wallet')
|
||||
OverflowButton:
|
||||
text: _('Preferences')
|
||||
overflow: action_overflow
|
||||
on_release: app.popup_dialog('settings')
|
||||
|
||||
ScreenManager:
|
||||
id: manager
|
||||
#tabs: Factory.ScreenTabs()
|
||||
ScreenTabs:
|
||||
id: tabs
|
||||
name: "tabs"
|
||||
|
||||
#on_current_screen:
|
||||
#spnr.text = args[1].name
|
||||
#idx = app.screen_names.index(args[1].name)
|
||||
#if idx > -1: app.hierarchy.append(idx)
|
||||
#args
|
||||
|
|
|
@ -9,11 +9,6 @@ from electrum.contacts import Contacts
|
|||
from electrum import bitcoin
|
||||
from electrum.util import profiler, print_error
|
||||
|
||||
from kivy.config import Config
|
||||
Config.set('modules', 'screen', 'droid2')
|
||||
Config.set('graphics', 'width', '480')
|
||||
Config.set('graphics', 'height', '840')
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.core.window import Window
|
||||
from kivy.logger import Logger
|
||||
|
@ -23,8 +18,7 @@ from kivy.properties import (OptionProperty, AliasProperty, ObjectProperty,
|
|||
from kivy.cache import Cache
|
||||
from kivy.clock import Clock
|
||||
from kivy.factory import Factory
|
||||
|
||||
from electrum_gui.kivy.uix.drawer import Drawer
|
||||
from kivy.metrics import inch, metrics
|
||||
|
||||
# lazy imports for factory so that widgets can be used in kv
|
||||
Factory.register('InstallWizard',
|
||||
|
@ -34,15 +28,31 @@ Factory.register('ELTextInput', module='electrum_gui.kivy.uix.screens')
|
|||
|
||||
|
||||
# delayed imports: for startup speed on android
|
||||
notification = app = Decimal = ref = format_satoshis = Builder = None
|
||||
inch = None
|
||||
notification = app = ref = format_satoshis = Builder = None
|
||||
util = False
|
||||
re = None
|
||||
|
||||
from decimal import Decimal
|
||||
import re
|
||||
|
||||
# register widget cache for keeping memory down timeout to forever to cache
|
||||
# the data
|
||||
Cache.register('electrum_widgets', timeout=0)
|
||||
|
||||
from kivy.uix.screenmanager import Screen
|
||||
from kivy.uix.tabbedpanel import TabbedPanel
|
||||
|
||||
class ElectrumScreen(Screen):
|
||||
fullscreen = BooleanProperty(False)
|
||||
#def add_widget(self, *args):
|
||||
# if 'content' in self.ids:
|
||||
# return self.ids.content.add_widget(*args)
|
||||
# return super(ElectrumScreen, self).add_widget(*args)
|
||||
|
||||
|
||||
Factory.register('TabbedCarousel', module='electrum_gui.kivy.uix.screens')
|
||||
|
||||
|
||||
|
||||
class ElectrumWindow(App):
|
||||
|
||||
def _get_bu(self):
|
||||
|
@ -102,11 +112,9 @@ class ElectrumWindow(App):
|
|||
:attr:`electrum_config` is a `ObjectProperty`, defaults to None.
|
||||
'''
|
||||
|
||||
status = StringProperty(_('Uninitialised'))
|
||||
'''The status of the connection should show the balance when connected
|
||||
status = StringProperty(_('Not Connected'))
|
||||
balance = StringProperty('')
|
||||
|
||||
:attr:`status` is a `StringProperty` defaults to 'uninitialised'
|
||||
'''
|
||||
|
||||
def _get_num_zeros(self):
|
||||
try:
|
||||
|
@ -136,11 +144,8 @@ class ElectrumWindow(App):
|
|||
return int(p * x)
|
||||
|
||||
|
||||
navigation_higherarchy = ListProperty([])
|
||||
'''This is a list of the current navigation higherarchy of the app used to
|
||||
navigate using back button.
|
||||
|
||||
:attr:`navigation_higherarchy` is s `ListProperty` defaults to []
|
||||
hierarchy = ListProperty([])
|
||||
'''used to navigate with the back button.
|
||||
'''
|
||||
|
||||
_orientation = OptionProperty('landscape',
|
||||
|
@ -239,6 +244,8 @@ class ElectrumWindow(App):
|
|||
global Builder
|
||||
if not Builder:
|
||||
from kivy.lang import Builder
|
||||
|
||||
|
||||
return Builder.load_file('gui/kivy/main.kv')
|
||||
|
||||
def _pause(self):
|
||||
|
@ -252,6 +259,7 @@ class ElectrumWindow(App):
|
|||
def on_start(self):
|
||||
''' This is the start point of the kivy ui
|
||||
'''
|
||||
Logger.info("dpi: {} {}".format(metrics.dpi, metrics.dpi_rounded))
|
||||
win = Window
|
||||
win.bind(size=self.on_size,
|
||||
on_keyboard=self.on_keyboard)
|
||||
|
@ -299,6 +307,14 @@ class ElectrumWindow(App):
|
|||
if self.wallet:
|
||||
self.wallet.stop_threads()
|
||||
|
||||
def on_back(self):
|
||||
try:
|
||||
self.hierarchy.pop()()
|
||||
except IndexError:
|
||||
# capture back button and pause app.
|
||||
self._pause()
|
||||
|
||||
|
||||
def on_keyboard_height(self, window, height):
|
||||
win = window
|
||||
active_widg = win.children[0]
|
||||
|
@ -351,6 +367,12 @@ class ElectrumWindow(App):
|
|||
self.init_ui()
|
||||
self.load_wallet(wallet)
|
||||
|
||||
def popup_dialog(self, name):
|
||||
popup = Builder.load_file('gui/kivy/uix/ui_screens/'+name+'.kv')
|
||||
popup.open()
|
||||
|
||||
|
||||
|
||||
@profiler
|
||||
def init_ui(self):
|
||||
''' Initialize The Ux part of electrum. This function performs the basic
|
||||
|
@ -366,13 +388,15 @@ class ElectrumWindow(App):
|
|||
self.completions = []
|
||||
|
||||
# setup UX
|
||||
self.screens = ['mainscreen',]
|
||||
self.screens = {}
|
||||
|
||||
#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('TabbedCarousel',
|
||||
# module='electrum_gui.kivy.uix.screens')
|
||||
|
||||
Factory.register('ScreenDashboard',
|
||||
module='electrum_gui.kivy.uix.screens')
|
||||
#Factory.register('EffectWidget',
|
||||
|
@ -386,17 +410,26 @@ class ElectrumWindow(App):
|
|||
|
||||
# preload widgets. Remove this if you want to load the widgets on demand
|
||||
Cache.append('electrum_widgets', 'AnimatedPopup', Factory.AnimatedPopup())
|
||||
Cache.append('electrum_widgets', 'TabbedCarousel', Factory.TabbedCarousel())
|
||||
|
||||
#Cache.append('electrum_widgets', 'TabbedCarousel', Factory.TabbedCarousel())
|
||||
|
||||
Cache.append('electrum_widgets', 'QRCodeWidget', Factory.QRCodeWidget())
|
||||
Cache.append('electrum_widgets', 'CSpinner', Factory.CSpinner())
|
||||
|
||||
# load and focus the ui
|
||||
#Load mainscreen
|
||||
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
|
||||
|
||||
#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
|
||||
|
||||
self.root.manager = self.root.ids['manager']
|
||||
self.recent_activity_card = None
|
||||
self.history_screen = None
|
||||
self.contacts_screen = None
|
||||
self.wallet_screen = None
|
||||
|
||||
#TODO
|
||||
# load left_menu
|
||||
|
@ -411,6 +444,7 @@ class ElectrumWindow(App):
|
|||
|
||||
self.wallet = None
|
||||
|
||||
|
||||
def create_quote_text(self, btc_balance, mode='normal'):
|
||||
'''
|
||||
'''
|
||||
|
@ -473,10 +507,6 @@ class ElectrumWindow(App):
|
|||
if not self.wallet:
|
||||
return
|
||||
|
||||
global Decimal
|
||||
if not Decimal:
|
||||
from decimal import Decimal
|
||||
|
||||
unconfirmed = ''
|
||||
quote_text = ''
|
||||
|
||||
|
@ -487,34 +517,38 @@ class ElectrumWindow(App):
|
|||
server_height = self.network.get_server_height()
|
||||
server_lag = self.network.get_local_height() - server_height
|
||||
if not self.wallet.up_to_date or server_height == 0:
|
||||
text = _("Synchronizing...")
|
||||
self.status = _("Synchronizing...")
|
||||
elif server_lag > 1:
|
||||
text = _("Server is lagging (%d blocks)"%server_lag)
|
||||
self.status = _("Server lagging (%d blocks)"%server_lag)
|
||||
else:
|
||||
c, u, x = self.wallet.get_account_balance(self.current_account)
|
||||
text = self.format_amount(c)
|
||||
self.balance = text
|
||||
if u:
|
||||
unconfirmed = " [%s unconfirmed]" %( self.format_amount(u, True).strip())
|
||||
if x:
|
||||
unmatured = " [%s unmatured]"%(self.format_amount(x, True).strip())
|
||||
self.balance = text.strip()
|
||||
quote_text = self.create_quote_text(Decimal(c+u+x)/100000000, mode='symbol') or ''
|
||||
self.status = self.balance
|
||||
else:
|
||||
text = _("Not connected")
|
||||
try:
|
||||
status_card = self.root.main_screen.ids.tabs.ids.\
|
||||
screen_dashboard.ids.status_card
|
||||
except AttributeError:
|
||||
return
|
||||
self.status = text.strip()
|
||||
self.status = _("Not connected")
|
||||
|
||||
return
|
||||
|
||||
print self.root.manager.ids
|
||||
|
||||
#try:
|
||||
status_card = self.root.main_screen.ids.tabs.ids.\
|
||||
screen_dashboard.ids.status_card
|
||||
#except AttributeError:
|
||||
# return
|
||||
|
||||
status_card.quote_text = quote_text.strip()
|
||||
status_card.uncomfirmed = unconfirmed.strip()
|
||||
|
||||
def format_amount(self, x, is_diff=False, whitespaces=False):
|
||||
'''
|
||||
'''
|
||||
global format_satoshis
|
||||
if not format_satoshis:
|
||||
from electrum.util import format_satoshis
|
||||
from electrum.util import format_satoshis
|
||||
return format_satoshis(x, is_diff, self.num_zeros,
|
||||
self.decimal_point, whitespaces)
|
||||
|
||||
|
@ -532,116 +566,16 @@ class ElectrumWindow(App):
|
|||
self.update_history_tab()
|
||||
self.update_contacts_tab()
|
||||
|
||||
def parse_histories(self, items):
|
||||
for item in items:
|
||||
tx_hash, conf, value, timestamp, balance = 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).replace(',','.')
|
||||
else:
|
||||
v_str = '--'
|
||||
|
||||
balance_str = self.format_amount(balance).replace(',','.')
|
||||
|
||||
if tx_hash:
|
||||
label, is_default_label = self.wallet.get_label(tx_hash)
|
||||
else:
|
||||
label = _('Pruned transaction outputs')
|
||||
is_default_label = False
|
||||
|
||||
yield (conf, icon, time_str, label, v_str, balance_str, tx_hash)
|
||||
|
||||
@profiler
|
||||
def update_history_tab(self, see_all=False):
|
||||
try:
|
||||
history_card = self.root.main_screen.ids.tabs.ids.\
|
||||
screen_dashboard.ids.recent_activity_card
|
||||
except AttributeError:
|
||||
return
|
||||
histories = self.parse_histories(reversed(
|
||||
self.wallet.get_history(self.current_account)))
|
||||
|
||||
# repopulate History Card
|
||||
last_widget = history_card.ids.content.children[-1]
|
||||
history_card.ids.content.clear_widgets()
|
||||
history_add = history_card.ids.content.add_widget
|
||||
history_add(last_widget)
|
||||
RecentActivityItem = Factory.RecentActivityItem
|
||||
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 = 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)
|
||||
|
||||
if self.history_screen:
|
||||
self.history_screen.update(see_all)
|
||||
|
||||
def update_contacts_tab(self):
|
||||
contact_list = self.root.main_screen.ids.tabs.ids.\
|
||||
screen_contacts.ids.contact_container
|
||||
#contact_list.clear_widgets()
|
||||
if self.contacts_screen:
|
||||
self.contacts_screen.update()
|
||||
|
||||
child = -1
|
||||
children = contact_list.children
|
||||
|
||||
for key in sorted(self.contacts.keys()):
|
||||
_type, address = self.contacts[key]
|
||||
label = self.wallet.labels.get(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 do_send(self):
|
||||
app = App.get_running_app()
|
||||
|
@ -650,9 +584,6 @@ class ElectrumWindow(App):
|
|||
label = unicode(scrn.message_e.text)
|
||||
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
|
||||
|
||||
|
@ -774,46 +705,23 @@ class ElectrumWindow(App):
|
|||
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))
|
||||
#Logger.info("size: {} {}".format(width, height))
|
||||
#Logger.info('orientation: {}'.format(self._orientation))
|
||||
#Logger.info('ui_mode: {}'.format(self._ui_mode))
|
||||
|
||||
def load_screen(self, index=0, direction='left', manager=None, switch=True):
|
||||
''' Load the appropriate screen as mentioned in the parameters.
|
||||
'''
|
||||
def load_screen(self, name, direction='left', manager=None):
|
||||
screen = self.screens.get(name)
|
||||
if screen is None:
|
||||
screen = Builder.load_file('gui/kivy/uix/ui_screens/' + name + '.kv')
|
||||
screen.name = name
|
||||
self.screens[name] = screen
|
||||
manager = manager or self.root.manager
|
||||
screen = Builder.load_file('gui/kivy/uix/ui_screens/'\
|
||||
+ self.screens[index] + '.kv')
|
||||
screen.name = self.screens[index]
|
||||
if switch:
|
||||
manager.switch_to(screen, direction=direction)
|
||||
return screen
|
||||
manager.switch_to(screen, direction=direction)
|
||||
|
||||
def load_next_screen(self):
|
||||
'''
|
||||
'''
|
||||
manager = root.manager
|
||||
try:
|
||||
self.load_screen(self.screens.index(manager.current_screen.name)+1,
|
||||
manager=manager)
|
||||
except IndexError:
|
||||
self.load_screen()
|
||||
|
||||
def load_previous_screen(self):
|
||||
''' Load the previous screen from disk.
|
||||
'''
|
||||
manager = root.manager
|
||||
try:
|
||||
self.load_screen(self.screens.index(manager.current_screen.name)-1,
|
||||
direction='right',
|
||||
manager=manager)
|
||||
except IndexError:
|
||||
pass
|
||||
def load_history(self):
|
||||
#Builder.load_file('gui/kivy/uix/ui_screens/history.kv')
|
||||
print "load history", self.root.manager.ids.history
|
||||
|
||||
def save_new_contact(self, address, label):
|
||||
address = unicode(address)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 475 KiB After Width: | Height: | Size: 468 KiB |
|
@ -1 +1 @@
|
|||
{"light-0.png": {"closebutton": [641, 591, 60, 43], "card_top": [901, 792, 32, 16], "tab_btn_disabled": [867, 483, 32, 32], "tab_btn_pressed": [901, 483, 32, 32], "bit_logo": [589, 728, 44, 51], "globe": [686, 267, 72, 72], "btn_send_nfc": [955, 793, 18, 15], "shadow_right": [975, 803, 32, 5], "logo_atom_dull": [773, 517, 64, 64], "create_act_text_active": [995, 638, 22, 10], "action_group_light": [431, 344, 33, 48], "tab": [390, 715, 64, 64], "logo": [296, 211, 128, 128], "qrcode": [2, 194, 145, 145], "close": [834, 810, 88, 88], "btn_create_act_disabled": [985, 911, 32, 32], "white_bg_round_top": [834, 788, 31, 20], "card_bottom": [867, 792, 32, 16], "confirmed": [839, 636, 64, 64], "overflow_btn_dn": [989, 520, 16, 10], "carousel_deselected": [760, 275, 64, 64], "network": [692, 467, 48, 48], "blue_bg_round_rb": [935, 495, 31, 20], "dropdown_background": [765, 599, 29, 35], "action_bar": [795, 479, 36, 36], "pen": [905, 517, 64, 64], "overflow_background": [796, 599, 29, 35], "arrow_back": [971, 650, 50, 50], "clock3": [641, 636, 64, 64], "contact": [971, 532, 49, 49], "star_big_inactive": [426, 211, 128, 128], "lightblue_bg_round_lb": [968, 495, 31, 20], "manualentry": [149, 205, 145, 134], "stepper_restore_password": [247, 464, 392, 117], "tab_disabled": [752, 233, 96, 32], "mail_icon": [522, 725, 65, 54], "tab_strip": [850, 233, 96, 32], "tab_btn": [833, 483, 32, 32], "btn_create_account": [948, 233, 64, 32], "btn_send_address": [935, 793, 18, 15], "add_contact": [742, 472, 51, 43], "gear": [2, 33, 105, 159], "wallets": [703, 594, 60, 40], "stepper_left": [247, 583, 392, 117], "nfc_stage_one": [324, 900, 489, 122], "nfc_clock": [2, 460, 243, 240], "btn_nfc": [752, 219, 13, 12], "textinput_active": [718, 784, 114, 114], "clock2": [958, 275, 64, 64], "nfc_phone": [556, 213, 128, 126], "clock4": [707, 636, 64, 64], "paste_icon": [945, 945, 75, 77], "shadow": [324, 715, 64, 64], "carousel_selected": [826, 275, 64, 64], "card": [686, 216, 64, 49], "unconfirmed": [456, 715, 64, 64], "info": [707, 517, 64, 64], "electrum_icon640": [2, 702, 320, 320], "action_button_group": [971, 520, 16, 10], "action_group_dark": [396, 344, 33, 48], "nfc": [839, 517, 64, 64], "contact_avatar": [641, 466, 49, 49], "clock1": [892, 275, 64, 64], "create_act_text": [971, 638, 22, 10], "icon_border": [641, 517, 64, 64], "stepper_full": [324, 781, 392, 117], "card_btn": [945, 911, 38, 32], "wallet": [635, 735, 49, 44], "important": [924, 810, 88, 88], "dialog": [1001, 495, 18, 20], "error": [815, 908, 128, 114], "stepper_restore_seed": [2, 341, 392, 117], "contact_overlay": [905, 636, 64, 64], "settings": [396, 394, 54, 64], "clock5": [773, 636, 64, 64]}}
|
||||
{"light-0.png": {"closebutton": [964, 855, 60, 43], "card_top": [964, 786, 32, 16], "tab_btn_disabled": [788, 483, 32, 32], "tab_btn_pressed": [856, 483, 32, 32], "bit_logo": [396, 407, 44, 51], "globe": [821, 628, 72, 72], "btn_send_nfc": [755, 290, 18, 15], "shadow_right": [895, 629, 32, 5], "logo_atom_dull": [654, 715, 64, 64], "action_group_light": [396, 357, 33, 48], "tab": [918, 715, 64, 64], "logo": [815, 906, 128, 116], "qrcode": [2, 194, 145, 145], "close": [641, 612, 88, 88], "btn_create_act_disabled": [754, 483, 32, 32], "white_bg_round_top": [956, 495, 31, 20], "card_bottom": [989, 499, 32, 16], "confirmed": [390, 715, 64, 64], "overflow_btn_dn": [995, 519, 16, 10], "carousel_deselected": [895, 636, 64, 64], "network": [556, 225, 48, 48], "blue_bg_round_rb": [890, 495, 31, 20], "dropdown_background": [659, 238, 29, 35], "action_bar": [945, 907, 36, 36], "pen": [786, 715, 64, 64], "overflow_background": [690, 238, 29, 35], "arrow_back": [971, 531, 50, 50], "clock3": [839, 517, 64, 64], "contact": [641, 466, 49, 49], "star_big_inactive": [296, 211, 128, 128], "lightblue_bg_round_lb": [923, 495, 31, 20], "manualentry": [149, 205, 145, 134], "stepper_restore_password": [247, 464, 392, 117], "tab_disabled": [755, 307, 96, 32], "mail_icon": [622, 285, 65, 54], "tab_strip": [853, 307, 96, 32], "tab_btn": [822, 483, 32, 32], "btn_create_account": [951, 307, 64, 32], "btn_send_address": [998, 787, 18, 15], "add_contact": [606, 230, 51, 43], "gear": [2, 33, 105, 159], "wallets": [692, 475, 60, 40], "stepper_left": [247, 583, 392, 117], "nfc_stage_one": [324, 900, 489, 122], "nfc_clock": [2, 460, 243, 240], "btn_nfc": [821, 614, 13, 12], "textinput_active": [848, 784, 114, 114], "clock2": [773, 517, 64, 64], "nfc_phone": [426, 213, 128, 126], "clock4": [905, 517, 64, 64], "paste_icon": [945, 945, 75, 77], "shadow": [852, 715, 64, 64], "carousel_selected": [641, 517, 64, 64], "card": [689, 290, 64, 49], "unconfirmed": [556, 275, 64, 64], "info": [588, 715, 64, 64], "electrum_icon640": [2, 702, 320, 320], "action_button_group": [1008, 719, 16, 10], "action_group_dark": [984, 731, 33, 48], "nfc": [720, 715, 64, 64], "contact_avatar": [964, 804, 49, 49], "clock1": [707, 517, 64, 64], "create_act_text_active": [984, 719, 22, 10], "icon_border": [522, 715, 64, 64], "stepper_full": [324, 781, 392, 117], "card_btn": [983, 911, 38, 32], "wallet": [442, 414, 49, 44], "important": [731, 612, 88, 88], "dialog": [641, 590, 18, 20], "error": [718, 784, 128, 114], "stepper_restore_seed": [2, 341, 392, 117], "contact_overlay": [456, 715, 64, 64], "settings": [961, 636, 54, 64], "create_act_text": [971, 519, 22, 10], "clock5": [324, 715, 64, 64]}}
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 9.1 KiB |
|
@ -1,319 +0,0 @@
|
|||
# source: http://stackoverflow.com/questions/2758159/how-to-embed-a-python-interpreter-in-a-pyqt-widget
|
||||
|
||||
import sys, os, re
|
||||
import traceback, platform
|
||||
from kivy.core.window import Keyboard
|
||||
from kivy.uix.textinput import TextInput
|
||||
from kivy.properties import StringProperty, ListProperty, DictProperty
|
||||
from kivy.clock import Clock
|
||||
|
||||
from electrum import util
|
||||
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
MONOSPACE_FONT = 'Lucida Console'
|
||||
elif platform.system() == 'Darwin':
|
||||
MONOSPACE_FONT = 'Monaco'
|
||||
else:
|
||||
MONOSPACE_FONT = 'monospace'
|
||||
|
||||
|
||||
class Console(TextInput):
|
||||
|
||||
prompt = StringProperty('>> ')
|
||||
'''String representing the Prompt message'''
|
||||
|
||||
startup_message = StringProperty('')
|
||||
'''Startup Message to be displayed in the Console if any'''
|
||||
|
||||
history = ListProperty([])
|
||||
'''History of the console'''
|
||||
|
||||
namespace = DictProperty({})
|
||||
'''Dict representing the current namespace of the console'''
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(Console, self).__init__(**kwargs)
|
||||
self.construct = []
|
||||
self.showMessage(self.startup_message)
|
||||
self.updateNamespace({'run':self.run_script})
|
||||
self.set_json(False)
|
||||
|
||||
def set_json(self, b):
|
||||
self.is_json = b
|
||||
|
||||
def run_script(self, filename):
|
||||
with open(filename) as f:
|
||||
script = f.read()
|
||||
result = eval(script, self.namespace, self.namespace)
|
||||
|
||||
def updateNamespace(self, namespace):
|
||||
self.namespace.update(namespace)
|
||||
|
||||
def showMessage(self, message):
|
||||
self.appendPlainText(message)
|
||||
self.newPrompt()
|
||||
|
||||
def clear(self):
|
||||
self.setPlainText('')
|
||||
self.newPrompt()
|
||||
|
||||
def newPrompt(self):
|
||||
if self.construct:
|
||||
prompt = '.' * len(self.prompt)
|
||||
else:
|
||||
prompt = self.prompt
|
||||
|
||||
self.completions_pos = self.cursor_index()
|
||||
self.completions_visible = False
|
||||
|
||||
self.appendPlainText(prompt)
|
||||
self.move_cursor_to('end')
|
||||
|
||||
def getCommand(self):
|
||||
curr_line = self._lines[-1]
|
||||
curr_line = curr_line.rstrip()
|
||||
curr_line = curr_line[len(self.prompt):]
|
||||
return curr_line
|
||||
|
||||
def setCommand(self, command):
|
||||
if self.getCommand() == command:
|
||||
return
|
||||
curr_line = self._lines[-1]
|
||||
last_pos = len(self.text)
|
||||
self.select_text(last_pos - len(curr_line) + len(self.prompt), last_pos)
|
||||
self.delete_selection()
|
||||
self.insert_text(command)
|
||||
|
||||
def show_completions(self, completions):
|
||||
if self.completions_visible:
|
||||
self.hide_completions()
|
||||
|
||||
self.move_cursor_to(self.completions_pos)
|
||||
|
||||
completions = map(lambda x: x.split('.')[-1], completions)
|
||||
t = '\n' + ' '.join(completions)
|
||||
if len(t) > 500:
|
||||
t = t[:500] + '...'
|
||||
self.insert_text(t)
|
||||
self.completions_end = self.cursor_index()
|
||||
|
||||
self.move_cursor_to('end')
|
||||
self.completions_visible = True
|
||||
|
||||
|
||||
def hide_completions(self):
|
||||
if not self.completions_visible:
|
||||
return
|
||||
self.move_cursor_to(self.completions_pos)
|
||||
l = self.completions_end - self.completions_pos
|
||||
for x in range(l):
|
||||
self.move_cursor_to('cursor_right')
|
||||
self.do_backspace()
|
||||
|
||||
self.move_cursor_to('end')
|
||||
self.completions_visible = False
|
||||
|
||||
def getConstruct(self, command):
|
||||
if self.construct:
|
||||
prev_command = self.construct[-1]
|
||||
self.construct.append(command)
|
||||
if not prev_command and not command:
|
||||
ret_val = '\n'.join(self.construct)
|
||||
self.construct = []
|
||||
return ret_val
|
||||
else:
|
||||
return ''
|
||||
else:
|
||||
if command and command[-1] == (':'):
|
||||
self.construct.append(command)
|
||||
return ''
|
||||
else:
|
||||
return command
|
||||
|
||||
def getHistory(self):
|
||||
return self.history
|
||||
|
||||
def setHisory(self, history):
|
||||
self.history = history
|
||||
|
||||
def addToHistory(self, command):
|
||||
if command and (not self.history or self.history[-1] != command):
|
||||
self.history.append(command)
|
||||
self.history_index = len(self.history)
|
||||
|
||||
def getPrevHistoryEntry(self):
|
||||
if self.history:
|
||||
self.history_index = max(0, self.history_index - 1)
|
||||
return self.history[self.history_index]
|
||||
return ''
|
||||
|
||||
def getNextHistoryEntry(self):
|
||||
if self.history:
|
||||
hist_len = len(self.history)
|
||||
self.history_index = min(hist_len, self.history_index + 1)
|
||||
if self.history_index < hist_len:
|
||||
return self.history[self.history_index]
|
||||
return ''
|
||||
|
||||
def getCursorPosition(self):
|
||||
return self.cursor[0] - len(self.prompt)
|
||||
|
||||
def setCursorPosition(self, position):
|
||||
self.cursor = (len(self.prompt) + position, self.cursor[1])
|
||||
|
||||
def register_command(self, c, func):
|
||||
methods = { c: func}
|
||||
self.updateNamespace(methods)
|
||||
|
||||
|
||||
def runCommand(self):
|
||||
command = self.getCommand()
|
||||
self.addToHistory(command)
|
||||
|
||||
command = self.getConstruct(command)
|
||||
|
||||
if command:
|
||||
tmp_stdout = sys.stdout
|
||||
|
||||
class stdoutProxy():
|
||||
def __init__(self, write_func):
|
||||
self.write_func = write_func
|
||||
self.skip = False
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def write(self, text):
|
||||
if not self.skip:
|
||||
stripped_text = text.rstrip('\n')
|
||||
self.write_func(stripped_text)
|
||||
self.skip = not self.skip
|
||||
|
||||
if type(self.namespace.get(command)) == type(lambda:None):
|
||||
self.appendPlainText("'%s' is a function. Type '%s()' to use it in the Python console."%(command, command))
|
||||
self.newPrompt()
|
||||
return
|
||||
|
||||
sys.stdout = stdoutProxy(self.appendPlainText)
|
||||
try:
|
||||
try:
|
||||
result = eval(command, self.namespace, self.namespace)
|
||||
if result != None:
|
||||
if self.is_json:
|
||||
util.print_json(result)
|
||||
else:
|
||||
self.appendPlainText(repr(result))
|
||||
except SyntaxError:
|
||||
exec command in self.namespace
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
traceback_lines = traceback.format_exc().split('\n')
|
||||
# Remove traceback mentioning this file, and a linebreak
|
||||
for i in (3,2,1,-1):
|
||||
traceback_lines.pop(i)
|
||||
self.appendPlainText('\n'.join(traceback_lines))
|
||||
sys.stdout = tmp_stdout
|
||||
self.newPrompt()
|
||||
self.set_json(False)
|
||||
|
||||
def _keyboard_on_key_down(self, window, keycode, text, modifiers):
|
||||
self._hide_cut_copy_paste()
|
||||
is_osx = sys.platform == 'darwin'
|
||||
# Keycodes on OSX:
|
||||
ctrl, cmd = 64, 1024
|
||||
key, key_str = keycode
|
||||
|
||||
if key == Keyboard.keycodes['tab']:
|
||||
self.completions()
|
||||
return
|
||||
|
||||
self.hide_completions()
|
||||
|
||||
if key == Keyboard.keycodes['enter']:
|
||||
self.runCommand()
|
||||
return
|
||||
if key == Keyboard.keycodes['home']:
|
||||
self.setCursorPosition(0)
|
||||
return
|
||||
if key == Keyboard.keycodes['pageup']:
|
||||
return
|
||||
elif key in (Keyboard.keycodes['left'], Keyboard.keycodes['backspace']):
|
||||
if self.getCursorPosition() == 0:
|
||||
return
|
||||
elif key == Keyboard.keycodes['up']:
|
||||
self.setCommand(self.getPrevHistoryEntry())
|
||||
return
|
||||
elif key == Keyboard.keycodes['down']:
|
||||
self.setCommand(self.getNextHistoryEntry())
|
||||
return
|
||||
elif key == Keyboard.keycodes['l'] and modifiers == ['ctrl']:
|
||||
self.clear()
|
||||
|
||||
super(Console, self)._keyboard_on_key_down(window, keycode, text, modifiers)
|
||||
|
||||
def completions(self):
|
||||
cmd = self.getCommand()
|
||||
lastword = re.split(' |\(|\)',cmd)[-1]
|
||||
beginning = cmd[0:-len(lastword)]
|
||||
|
||||
path = lastword.split('.')
|
||||
ns = self.namespace.keys()
|
||||
|
||||
if len(path) == 1:
|
||||
ns = ns
|
||||
prefix = ''
|
||||
else:
|
||||
obj = self.namespace.get(path[0])
|
||||
prefix = path[0] + '.'
|
||||
ns = dir(obj)
|
||||
|
||||
|
||||
completions = []
|
||||
for x in ns:
|
||||
if x[0] == '_':continue
|
||||
xx = prefix + x
|
||||
if xx.startswith(lastword):
|
||||
completions.append(xx)
|
||||
completions.sort()
|
||||
|
||||
if not completions:
|
||||
self.hide_completions()
|
||||
elif len(completions) == 1:
|
||||
self.hide_completions()
|
||||
self.setCommand(beginning + completions[0])
|
||||
else:
|
||||
# find common prefix
|
||||
p = os.path.commonprefix(completions)
|
||||
if len(p)>len(lastword):
|
||||
self.hide_completions()
|
||||
self.setCommand(beginning + p)
|
||||
else:
|
||||
self.show_completions(completions)
|
||||
|
||||
# NEW
|
||||
def setPlainText(self, message):
|
||||
"""Equivalent to QT version"""
|
||||
self.text = message
|
||||
|
||||
# NEW
|
||||
def appendPlainText(self, message):
|
||||
"""Equivalent to QT version"""
|
||||
if len(self.text) == 0:
|
||||
self.text = message
|
||||
else:
|
||||
if message:
|
||||
self.text += '\n' + message
|
||||
|
||||
# NEW
|
||||
def move_cursor_to(self, pos):
|
||||
"""Aggregate all cursor moving functions"""
|
||||
if isinstance(pos, int):
|
||||
self.cursor = self.get_cursor_from_index(pos)
|
||||
elif pos in ('end', 'pgend', 'pageend'):
|
||||
def updt_cursor(*l):
|
||||
self.cursor = self.get_cursor_from_index(self.text)
|
||||
Clock.schedule_once(updt_cursor)
|
||||
else: # cursor_home, cursor_end, ... (see docs)
|
||||
self.do_cursor_movement(pos)
|
|
@ -91,9 +91,10 @@ class RecentActivityDialog(CarouselDialog):
|
|||
'''
|
||||
'''
|
||||
def on_activate(self):
|
||||
|
||||
# animate to first slide
|
||||
carousel = self.carousel_content.carousel
|
||||
carousel.load_slide(carousel.slides[0])
|
||||
#carousel = self.carousel_content.carousel
|
||||
#carousel.load_slide(carousel.slides[0])
|
||||
|
||||
item = self.item
|
||||
try:
|
||||
|
|
|
@ -7,6 +7,7 @@ from kivy.properties import (ObjectProperty, DictProperty, NumericProperty,
|
|||
from kivy.lang import Builder
|
||||
from kivy.factory import Factory
|
||||
|
||||
from electrum.i18n import _
|
||||
|
||||
# Delayed imports
|
||||
app = None
|
||||
|
@ -17,6 +18,9 @@ class CScreen(Factory.Screen):
|
|||
__events__ = ('on_activate', 'on_deactivate', 'on_enter', 'on_leave')
|
||||
|
||||
action_view = ObjectProperty(None)
|
||||
loaded = False
|
||||
kvname = None
|
||||
app = App.get_running_app()
|
||||
|
||||
def _change_action_view(self):
|
||||
app = App.get_running_app()
|
||||
|
@ -31,26 +35,43 @@ class CScreen(Factory.Screen):
|
|||
def on_enter(self):
|
||||
# FIXME: use a proper event don't use animation time of screen
|
||||
Clock.schedule_once(lambda dt: self.dispatch('on_activate'), .25)
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
def on_activate(self):
|
||||
Clock.schedule_once(lambda dt: self._change_action_view())
|
||||
|
||||
if self.kvname and not self.loaded:
|
||||
print "loading:" + self.kvname
|
||||
self.screen = Builder.load_file('gui/kivy/uix/ui_screens/' + self.kvname + '.kv')
|
||||
self.add_widget(self.screen)
|
||||
self.loaded = True
|
||||
self.update()
|
||||
setattr(self.app, self.kvname + '_screen', self)
|
||||
|
||||
#app.history_screen = screen
|
||||
#app.recent_activity_card = screen.ids.recent_activity_card
|
||||
#app.update_history_tab()
|
||||
|
||||
#Clock.schedule_once(lambda dt: self._change_action_view())
|
||||
|
||||
def on_leave(self):
|
||||
self.dispatch('on_deactivate')
|
||||
|
||||
def on_deactivate(self):
|
||||
Clock.schedule_once(lambda dt: self._change_action_view())
|
||||
pass
|
||||
#Clock.schedule_once(lambda dt: self._change_action_view())
|
||||
|
||||
|
||||
class ScreenDashboard(CScreen):
|
||||
''' Dashboard screen: Used to display the main dashboard.
|
||||
'''
|
||||
class HistoryScreen(CScreen):
|
||||
|
||||
tab = ObjectProperty(None)
|
||||
kvname = 'history'
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.ra_dialog = None
|
||||
super(ScreenDashboard, self).__init__(**kwargs)
|
||||
super(HistoryScreen, self).__init__(**kwargs)
|
||||
|
||||
def show_tx_details(self, item):
|
||||
ra_dialog = Cache.get('electrum_widgets', 'RecentActivityDialog')
|
||||
|
@ -64,6 +85,83 @@ class ScreenDashboard(CScreen):
|
|||
ra_dialog.item = item
|
||||
ra_dialog.open()
|
||||
|
||||
def parse_histories(self, items):
|
||||
for item in items:
|
||||
tx_hash, conf, value, timestamp, balance = 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.app.format_amount(value, True).replace(',','.')
|
||||
else:
|
||||
v_str = '--'
|
||||
|
||||
balance_str = self.app.format_amount(balance).replace(',','.')
|
||||
|
||||
if tx_hash:
|
||||
label, is_default_label = self.app.wallet.get_label(tx_hash)
|
||||
else:
|
||||
label = _('Pruned transaction outputs')
|
||||
is_default_label = False
|
||||
|
||||
yield (conf, icon, time_str, label, v_str, balance_str, tx_hash)
|
||||
|
||||
def update(self, see_all=False):
|
||||
|
||||
history_card = self.screen.ids.recent_activity_card
|
||||
histories = self.parse_histories(reversed(
|
||||
self.app.wallet.get_history(self.app.current_account)))
|
||||
# repopulate History Card
|
||||
last_widget = history_card.ids.content.children[-1]
|
||||
history_card.ids.content.clear_widgets()
|
||||
history_add = history_card.ids.content.add_widget
|
||||
history_add(last_widget)
|
||||
RecentActivityItem = Factory.RecentActivityItem
|
||||
|
||||
from weakref import ref
|
||||
from decimal import Decimal
|
||||
|
||||
get_history_rate = self.app.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 = 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)
|
||||
|
||||
|
||||
class ScreenAddress(CScreen):
|
||||
'''This is the dialog that shows a carousel of the currently available
|
||||
|
@ -95,22 +193,19 @@ class ScreenPassword(Factory.Screen):
|
|||
pass
|
||||
|
||||
|
||||
class MainScreen(Factory.Screen):
|
||||
pass
|
||||
|
||||
|
||||
class ScreenSend(CScreen):
|
||||
|
||||
class SendScreen(CScreen):
|
||||
kvname = 'send'
|
||||
def set_qr_data(self, uri):
|
||||
self.ids.payto_e.text = uri.get('address', '')
|
||||
self.ids.message_e.text = uri.get('message', '')
|
||||
self.ids.amount_e.text = uri.get('amount', '')
|
||||
|
||||
class ScreenReceive(CScreen):
|
||||
pass
|
||||
class ReceiveScreen(CScreen):
|
||||
kvname = 'receive'
|
||||
|
||||
|
||||
class ScreenContacts(CScreen):
|
||||
class ContactsScreen(CScreen):
|
||||
kvname = 'contacts'
|
||||
|
||||
def add_new_contact(self):
|
||||
dlg = Cache.get('electrum_widgets', 'NewContactDialog')
|
||||
|
@ -119,6 +214,25 @@ class ScreenContacts(CScreen):
|
|||
Cache.append('electrum_widgets', 'NewContactDialog', dlg)
|
||||
dlg.open()
|
||||
|
||||
def update(self):
|
||||
contact_list = self.screen.ids.contact_container
|
||||
contact_list.clear_widgets()
|
||||
child = -1
|
||||
children = contact_list.children
|
||||
for key in sorted(self.app.contacts.keys()):
|
||||
_type, value = self.app.contacts[key]
|
||||
child += 1
|
||||
try:
|
||||
if children[child].label == value:
|
||||
continue
|
||||
except IndexError:
|
||||
pass
|
||||
ci = Factory.ContactItem()
|
||||
ci.address = key
|
||||
ci.label = value
|
||||
contact_list.add_widget(ci)
|
||||
|
||||
|
||||
|
||||
class CSpinner(Factory.Spinner):
|
||||
'''CustomDropDown that allows fading out the dropdown
|
||||
|
@ -139,7 +253,7 @@ class CSpinner(Factory.Spinner):
|
|||
|
||||
|
||||
class TabbedCarousel(Factory.TabbedPanel):
|
||||
'''Custom TabbedOanel using a carousel used in the Main Screen
|
||||
'''Custom TabbedPanel using a carousel used in the Main Screen
|
||||
'''
|
||||
|
||||
carousel = ObjectProperty(None)
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<ContactImage@Widget>:
|
||||
source: 'atlas://gui/kivy/theming/light/contact_avatar'
|
||||
size_hint_x: None
|
||||
width: self.height
|
||||
canvas:
|
||||
Color:
|
||||
rgba: 1, 1, 1, 1
|
||||
Ellipse:
|
||||
source: root.source
|
||||
size: self.width + dp(6), self.height + dp(6)
|
||||
pos: self.x - dp(3), self.y - dp(3)
|
||||
Ellipse:
|
||||
source: 'atlas://gui/kivy/theming/light/contact_overlay'
|
||||
size: self.width + dp(11), self.height + dp(11)
|
||||
pos: self.x - dp(5.5), self.y - dp(5.5)
|
||||
|
||||
<ContactLabel@Label>
|
||||
color: .305, .309, .309, 1
|
||||
text_size: self.size
|
||||
halign: 'left'
|
||||
valign: 'middle'
|
||||
|
||||
<ContactSeperator@Widget>
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: .890, .890, .890, 1
|
||||
Rectangle:
|
||||
size: self.size
|
||||
pos: self.x, self.y + dp(9)
|
||||
size_hint: None, None
|
||||
size: '1dp', '22dp'
|
||||
pos_hint_y: .5
|
||||
|
||||
<ContactTextInput@TextInput>
|
||||
background_normal: self.background_down
|
||||
background_down: 'atlas://gui/kivy/theming/light/tab_btn'
|
||||
size_hint_y: None
|
||||
height: '22dp'
|
||||
|
||||
<ContactBitLogo@Image>
|
||||
source: 'atlas://gui/kivy/theming/light/bit_logo'
|
||||
size_hint_x: None
|
||||
width: '32dp'
|
||||
|
||||
<ContactItem@BoxLayout>
|
||||
address: ''
|
||||
label: ''
|
||||
tx_amt: 0
|
||||
size_hint_y: None
|
||||
height: '65dp'
|
||||
padding: dp(12)
|
||||
spacing: dp(5)
|
||||
canvas.before:
|
||||
#Color:
|
||||
# rgba: 1, 1, 1, 1
|
||||
Rectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
ContactImage:
|
||||
id: contact_image
|
||||
Widget:
|
||||
size_hint_x: None
|
||||
width: '9dp'
|
||||
ContactLabel:
|
||||
id: contact_address
|
||||
text: root.address
|
||||
ContactSeperator:
|
||||
ContactLabel:
|
||||
text: root.label
|
||||
#ContactBitLogo:
|
||||
|
||||
ContactsScreen:
|
||||
name: 'contacts'
|
||||
on_activate:
|
||||
if not self.action_view:\
|
||||
self.action_view = app.root.main_screen.ids.tabs.ids.screen_dashboard.action_view
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ContactTextInput:
|
||||
ScrollView:
|
||||
canvas.before:
|
||||
#Color:
|
||||
# rgba: .8901, .8901, .8901, 1
|
||||
Rectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: contact_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
spacing: '1dp'
|
|
@ -0,0 +1,224 @@
|
|||
#:import _ electrum.i18n._
|
||||
#:import Factory kivy.factory.Factory
|
||||
#:set font_light 'data/fonts/Roboto-Condensed.ttf'
|
||||
#:set btc_symbol unichr(171)
|
||||
#:set mbtc_symbol unichr(187)
|
||||
|
||||
|
||||
<Card@GridLayout>
|
||||
cols: 1
|
||||
padding: '12dp' , '22dp', '12dp' , '12dp'
|
||||
spacing: '12dp'
|
||||
size_hint: 1, None
|
||||
height: max(100, self.minimum_height)
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 1, 1, 1, 1
|
||||
BorderImage:
|
||||
border: 18, 18, 18, 18
|
||||
source: 'atlas://gui/kivy/theming/light/card'
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
|
||||
<CardLabel@Label>
|
||||
color: 0.45, 0.45, 0.45, 1
|
||||
size_hint: 1, None
|
||||
text: ''
|
||||
text_size: self.width, None
|
||||
height: self.texture_size[1]
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<CardButton@Button>
|
||||
background_normal: 'atlas://gui/kivy/theming/light/card_btn'
|
||||
bold: True
|
||||
font_size: '10sp'
|
||||
color: 0.699, 0.699, 0.699, 1
|
||||
size_hint: None, None
|
||||
size: self.texture_size[0] + dp(32), self.texture_size[1] + dp(7)
|
||||
|
||||
|
||||
<CardItem@ButtonBehavior+GridLayout>
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0.192, .498, 0.745, 1 if self.state == 'down' else 0
|
||||
Rectangle
|
||||
size: self.size
|
||||
pos: self.x, self.y + dp(5)
|
||||
cols: 1
|
||||
padding: '2dp', '2dp'
|
||||
spacing: '2dp'
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
|
||||
<RecentActivityItem@CardItem>
|
||||
icon: 'atlas://gui/kivy/theming/light/important'
|
||||
address:'no address set'
|
||||
amount: '+0.00'
|
||||
balance: 'xyz'# balance_after
|
||||
amount_color: '#DB3627' if float(self.amount) < 0 else '#2EA442'
|
||||
confirmations: 0
|
||||
date: '0/0/0'
|
||||
quote_text: '.'
|
||||
|
||||
spacing: '9dp'
|
||||
on_release:
|
||||
app.history_screen.show_tx_details(root)
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
Image:
|
||||
id: icon
|
||||
source: root.icon
|
||||
size_hint: None, 1
|
||||
width: self.height *.54
|
||||
mipmap: True
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
CardLabel:
|
||||
shorten: True
|
||||
text: root.address
|
||||
markup: False
|
||||
text_size: self.size
|
||||
CardLabel:
|
||||
color: .699, .699, .699, 1
|
||||
text: root.date
|
||||
font_size: '12sp'
|
||||
Widget
|
||||
CardLabel:
|
||||
halign: 'right'
|
||||
font_size: '13sp'
|
||||
size_hint: None, 1
|
||||
width: '90sp'
|
||||
markup: True
|
||||
font_name: font_light
|
||||
text:
|
||||
u'[color={amount_color}]{sign}{symbol}{amount}[/color]\n'\
|
||||
u'[color=#B2B3B3][size=12sp]{qt}[/size]'\
|
||||
u'[/color]'.format(amount_color=root.amount_color,\
|
||||
amount=root.amount[1:], qt=root.quote_text, sign=root.amount[0],\
|
||||
symbol=btc_symbol if app.base_unit == 'BTC' else mbtc_symbol)
|
||||
CardSeparator
|
||||
|
||||
<CardRecentActivity@Card>
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: lbl.height
|
||||
CardLabel:
|
||||
id: lbl
|
||||
text: _('RECENT ACTIVITY')
|
||||
CardButton:
|
||||
id: btn_see_all
|
||||
disabled: True if not self.opacity else False
|
||||
text: _('SEE ALL')
|
||||
font_size: '12sp'
|
||||
on_release: app.update_history_tab(see_all=True)
|
||||
GridLayout:
|
||||
id: content
|
||||
spacing: '7dp'
|
||||
cols: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
CardSeparator
|
||||
|
||||
<CardPaymentRequest@Card>
|
||||
CardLabel:
|
||||
text: _('PAYMENT REQUEST')
|
||||
CardSeparator:
|
||||
|
||||
<CardStatusInfo@Card>
|
||||
padding: '12dp' , '12dp'
|
||||
status: app.status
|
||||
quote_text: ''
|
||||
unconfirmed: ''
|
||||
cols: 2
|
||||
FloatLayout
|
||||
anchor_x: 'left'
|
||||
size_hint: 1, None
|
||||
height: '82dp'
|
||||
IconButton:
|
||||
mipmap: True
|
||||
pos_hint: {'x': 0, 'center_y': .45}
|
||||
color: .90, .90, .90, 1
|
||||
source: 'atlas://gui/kivy/theming/light/qrcode'
|
||||
size_hint: None, .85
|
||||
width: self.height
|
||||
on_release:
|
||||
dlg = Cache.get('electrum_widgets', 'WalletAddressesDialog')
|
||||
|
||||
if not dlg:\
|
||||
Factory.register('WalletAddressesDialog', module='electrum_gui.kivy.uix.dialogs.carousel_dialog');\
|
||||
dlg = Factory.WalletAddressesDialog();\
|
||||
Cache.append('electrum_widgets', 'WalletAddressesDialog', dlg)
|
||||
|
||||
dlg.open()
|
||||
CardLabel:
|
||||
id: top_label
|
||||
halign: 'right'
|
||||
valign: 'top'
|
||||
bold: True
|
||||
pos_hint: {'top': 1, 'right': 1}
|
||||
font_name: font_light
|
||||
#balance_in_numbers: bool(ord(root.status[0]) not in range(ord('A'), ord('z')))
|
||||
balance_in_numbers: True
|
||||
font_size: '50sp' if self.balance_in_numbers else '30sp'
|
||||
text_size: self.width, root.height/2
|
||||
text:
|
||||
u'[color=#4E4F4F]{}{}[/color]'\
|
||||
.format('' if not self.balance_in_numbers else\
|
||||
(btc_symbol if app.base_unit == 'BTC' else mbtc_symbol), root.status)
|
||||
BoxLayout
|
||||
pos_hint: {'y': 0, 'right': 1}
|
||||
spacing: '5dp'
|
||||
CardLabel
|
||||
halign: 'right'
|
||||
markup: True
|
||||
font_size: '22dp'
|
||||
font_name: font_light
|
||||
text: u'[color=#c3c3c3]{}[/color]'.format(root.quote_text)
|
||||
IconButton
|
||||
color: .698, .698, .698, 1
|
||||
source: 'atlas://gui/kivy/theming/light/gear'
|
||||
size_hint_y: None
|
||||
height: '28dp'
|
||||
opacity: .5 if self.state == 'down' else 1
|
||||
on_release:
|
||||
dlg = Cache.get('electrum_widgets', 'CurrencySelectionDialog')
|
||||
|
||||
if not dlg:\
|
||||
Factory.register('SelectionDialog', module='electrum_gui.kivy.uix.dialogs');\
|
||||
dlg = Factory.CurrencySelectionDialog();\
|
||||
Cache.append('electrum_widgets', 'CurrencySelectionDialog', dlg)
|
||||
|
||||
dlg.open()
|
||||
|
||||
|
||||
|
||||
HistoryScreen:
|
||||
name: 'history'
|
||||
content: content
|
||||
ScrollView:
|
||||
id: content
|
||||
do_scroll_x: False
|
||||
GridLayout
|
||||
id: grid
|
||||
cols: 1 #if root.width < root.height else 2
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
padding: '12dp'
|
||||
spacing: '12dp'
|
||||
#GridLayout:
|
||||
# cols: 1
|
||||
# size_hint: 1, None
|
||||
# height: self.minimum_height
|
||||
# spacing: '12dp'
|
||||
# orientation: 'vertical'
|
||||
# CardStatusInfo:
|
||||
# id: status_card
|
||||
# CardPaymentRequest:
|
||||
# id: payment_card
|
||||
CardRecentActivity:
|
||||
id: recent_activity_card
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
Popup:
|
||||
id: network
|
||||
title: _('Network')
|
||||
BoxLayout:
|
||||
|
||||
Button:
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
text: 'close'
|
||||
on_release: network.dismiss()
|
|
@ -0,0 +1,171 @@
|
|||
#:import _ electrum.i18n._
|
||||
#:import Decimal decimal.Decimal
|
||||
#:set btc_symbol unichr(171)
|
||||
#:set mbtc_symbol unichr(187)
|
||||
#:set font_light 'data/fonts/Roboto-Condensed.ttf'
|
||||
|
||||
|
||||
|
||||
ReceiveScreen:
|
||||
name: 'receive'
|
||||
mode: 'qr'
|
||||
on_mode: if args[1] == 'nfc': from electrum_gui.kivy.nfc_scanner import NFCScanner
|
||||
action_view: Factory.ReceiveActionView()
|
||||
|
||||
#on_activate:
|
||||
# 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.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'
|
||||
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')
|
||||
Button:
|
||||
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:
|
||||
id: action_previous
|
||||
width: '32dp'
|
||||
ActionButton:
|
||||
id: action_logo
|
||||
important: True
|
||||
size_hint: 1, 1
|
||||
markup: True
|
||||
mipmap: True
|
||||
bold: True
|
||||
markup: True
|
||||
color: 1, 1, 1, 1
|
||||
text:
|
||||
"[color=#777777][sub] [sup][size=9dp]{}[/size][/sup][/sub]{}[/color]"\
|
||||
.format(app.base_unit, app.status)
|
||||
font_size: '22dp'
|
||||
minimum_width: '1dp'
|
||||
Butt_star:
|
||||
id: but_star
|
||||
on_release:
|
||||
if self.state == 'down':\
|
||||
app.show_info_bubble(\
|
||||
text='[b]Expert mode on[/b]\n you can now select your address',\
|
||||
icon='atlas://gui/kivy/theming/light/star_big_inactive',\
|
||||
duration=1, arrow_pos='', width='250dp')
|
|
@ -0,0 +1,243 @@
|
|||
#:import _ electrum.i18n._
|
||||
#:import Decimal decimal.Decimal
|
||||
|
||||
#:import Factory kivy.factory.Factory
|
||||
#:set btc_symbol unichr(171)
|
||||
#:set mbtc_symbol unichr(187)
|
||||
#:set font_light 'data/fonts/Roboto-Condensed.ttf'
|
||||
|
||||
<SendActionView@ActionView>
|
||||
foreground_color: (.466, .466, .466, 1)
|
||||
color_active: (0.235, .588, .89, 1)
|
||||
WalletActionPrevious:
|
||||
id: action_previous
|
||||
width: but_star.width
|
||||
ActionButton:
|
||||
id: action_logo
|
||||
important: True
|
||||
size_hint: 1, 1
|
||||
markup: True
|
||||
mipmap: True
|
||||
bold: True
|
||||
markup: True
|
||||
color: 1, 1, 1, 1
|
||||
text:
|
||||
"[color=#777777][sub] [sup][size=9dp]{}[/size][/sup][/sub]{}[/color]"\
|
||||
.format(app.base_unit, app.status)
|
||||
font_size: '22dp'
|
||||
minimum_width: '1dp'
|
||||
Butt_star:
|
||||
id: but_star
|
||||
on_release:
|
||||
if self.state == 'down':\
|
||||
app.show_info_bubble(\
|
||||
text='[b]Expert mode on[/b]\n you can now select your address',\
|
||||
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'
|
||||
|
||||
|
||||
SendScreen:
|
||||
|
||||
mode: 'address'
|
||||
name: 'send'
|
||||
action_view: Factory.SendActionView()
|
||||
#on_deactivate:
|
||||
# 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_per_kb(app.gui_object.config)) 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: app.scan_qr(on_complete=root.set_qr_data)
|
||||
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: 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
|
||||
Button:
|
||||
#background_color: (1, 1, 1, 1) if self.disabled else ((.258, .80, .388, 1) if self.state == 'normal' else (.203, .490, .741, 1))
|
||||
text: _('Send')
|
||||
size_hint_y: None
|
||||
height: '38dp'
|
||||
disabled: False
|
||||
on_release: app.do_send()
|
||||
Widget
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
Popup:
|
||||
id: settings
|
||||
title: _('Settings')
|
||||
BoxLayout:
|
||||
|
||||
Button:
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
text: 'Button normal'
|
||||
|
||||
Button:
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
text: 'Button down'
|
||||
state: 'down'
|
||||
|
||||
Button:
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
text: 'Button disabled'
|
||||
disabled: True
|
||||
|
||||
Button:
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
text: 'close'
|
||||
on_release: settings.dismiss()
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
|
||||
<WalletSelector@BlueSpinner>
|
||||
icon: 'atlas://gui/kivy/theming/light/wallet'
|
||||
values: ('default Wallet',)
|
||||
text: _('Select your wallet')
|
||||
|
||||
|
||||
ElectrumScreen:
|
||||
|
||||
WalletSelector:
|
||||
id: wallet_selection
|
||||
size_hint: 1, None
|
||||
height: blue_bottom.item_height if app.expert_mode else 0
|
Loading…
Reference in New Issue