kivy: split mainscreen.kv into dynamically loaded .kv files

This commit is contained in:
ThomasV 2015-10-06 09:59:29 +02:00
parent d68042e76e
commit 65ecbf990d
15 changed files with 1428 additions and 563 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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'

View File

@ -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

View File

@ -0,0 +1,10 @@
Popup:
id: network
title: _('Network')
BoxLayout:
Button:
size_hint_y: None
height: '48dp'
text: 'close'
on_release: network.dismiss()

View File

@ -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')

View File

@ -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

View File

@ -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()

View File

@ -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