From c750ec153beb13b15f3ed955ecfec2f4e376e4bc Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 6 Mar 2018 14:58:00 +0100 Subject: [PATCH 1/4] new kivy gui --- gui/kivy/main.kv | 20 +-- gui/kivy/main_window.py | 19 ++- gui/kivy/uix/context_menu.py | 1 - gui/kivy/uix/dialogs/addresses.py | 212 ++++++++++++++++++++++++++++ gui/kivy/uix/dialogs/invoices.py | 172 ++++++++++++++++++++++ gui/kivy/uix/dialogs/requests.py | 157 ++++++++++++++++++++ gui/kivy/uix/screens.py | 201 +------------------------- gui/kivy/uix/ui_screens/address.kv | 90 ------------ gui/kivy/uix/ui_screens/invoices.kv | 66 --------- gui/kivy/uix/ui_screens/receive.kv | 23 ++- gui/kivy/uix/ui_screens/requests.kv | 66 --------- gui/kivy/uix/ui_screens/send.kv | 23 +-- 12 files changed, 598 insertions(+), 452 deletions(-) create mode 100644 gui/kivy/uix/dialogs/addresses.py create mode 100644 gui/kivy/uix/dialogs/invoices.py create mode 100644 gui/kivy/uix/dialogs/requests.py delete mode 100644 gui/kivy/uix/ui_screens/address.kv delete mode 100644 gui/kivy/uix/ui_screens/invoices.kv delete mode 100644 gui/kivy/uix/ui_screens/requests.kv diff --git a/gui/kivy/main.kv b/gui/kivy/main.kv index 95cfe8def..83b880d73 100644 --- a/gui/kivy/main.kv +++ b/gui/kivy/main.kv @@ -372,9 +372,6 @@ tab_height: '48dp' tab_width: panel.width/3 strip_border: 0, 0, 0, 0 - InvoicesScreen: - id: invoices_screen - tab: invoices_tab SendScreen: id: send_screen tab: send_tab @@ -384,29 +381,18 @@ ReceiveScreen: id: receive_screen tab: receive_tab - AddressScreen: - id: address_screen - tab: address_tab - CleanHeader: - id: invoices_tab - text: _('Invoices') - slide: 0 CleanHeader: id: send_tab text: _('Send') - slide: 1 + slide: 0 CleanHeader: id: history_tab text: _('Balance') - slide: 2 + slide: 1 CleanHeader: id: receive_tab text: _('Receive') - slide: 3 - CleanHeader: - id: address_tab - text: _('Addresses') - slide: 4 + slide: 2 diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py index 7ca40bf8d..c719907c8 100644 --- a/gui/kivy/main_window.py +++ b/gui/kivy/main_window.py @@ -824,7 +824,6 @@ class ElectrumWindow(App): d = LabelDialog(_('Enter description'), text, callback) d.open() - @profiler def amount_dialog(self, screen, show_max): from .uix.dialogs.amount_dialog import AmountDialog amount = screen.amount @@ -836,6 +835,24 @@ class ElectrumWindow(App): popup = AmountDialog(show_max, amount, cb) popup.open() + def invoices_dialog(self, screen): + from .uix.dialogs.invoices import InvoicesDialog + popup = InvoicesDialog(self, screen, None) + popup.update() + popup.open() + + def requests_dialog(self, screen): + from .uix.dialogs.requests import RequestsDialog + popup = RequestsDialog(self, screen, None) + popup.update() + popup.open() + + def addresses_dialog(self, screen): + from .uix.dialogs.addresses import AddressesDialog + popup = AddressesDialog(self, screen, None) + popup.update() + popup.open() + def fee_dialog(self, label, dt): from .uix.dialogs.fee_dialog import FeeDialog def cb(): diff --git a/gui/kivy/uix/context_menu.py b/gui/kivy/uix/context_menu.py index dee0212af..884a2098e 100644 --- a/gui/kivy/uix/context_menu.py +++ b/gui/kivy/uix/context_menu.py @@ -47,7 +47,6 @@ class ContextMenu(Bubble): l = MenuItem() l.text = _(k) def func(f=v): - Clock.schedule_once(lambda dt: self.hide(), 0.1) Clock.schedule_once(lambda dt: f(obj), 0.15) l.on_release = func self.ids.buttons.add_widget(l) diff --git a/gui/kivy/uix/dialogs/addresses.py b/gui/kivy/uix/dialogs/addresses.py new file mode 100644 index 000000000..1866251f2 --- /dev/null +++ b/gui/kivy/uix/dialogs/addresses.py @@ -0,0 +1,212 @@ +from kivy.app import App +from kivy.factory import Factory +from kivy.properties import ObjectProperty +from kivy.lang import Builder +from decimal import Decimal + +Builder.load_string(''' + + text_size: self.width, None + halign: 'left' + valign: 'top' + + + address: '' + memo: '' + amount: '' + status: '' + BoxLayout: + spacing: '8dp' + height: '32dp' + orientation: 'vertical' + Widget + AddressLabel: + text: root.address + shorten: True + Widget + AddressLabel: + text: (root.amount if root.status == 'Funded' else root.status) + ' ' + root.memo + color: .699, .699, .699, 1 + font_size: '13sp' + shorten: True + Widget + + + id: popup + title: _('Addresses') + message: '' + pr_status: 'Pending' + show_change: 0 + show_used: 0 + on_message: + self.parent.update() + BoxLayout: + id:box + padding: '12dp', '70dp', '12dp', '12dp' + spacing: '12dp' + orientation: 'vertical' + size_hint: 1, 1.1 + BoxLayout: + spacing: '6dp' + size_hint: 1, None + orientation: 'horizontal' + AddressFilter: + opacity: 1 + size_hint: 1, None + height: self.minimum_height + spacing: '5dp' + AddressButton: + id: search + text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change] + on_release: + root.show_change = (root.show_change + 1) % 3 + Clock.schedule_once(lambda dt: root.update()) + AddressFilter: + opacity: 1 + size_hint: 1, None + height: self.minimum_height + spacing: '5dp' + AddressButton: + id: search + text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used] + on_release: + root.show_used = (root.show_used + 1) % 4 + Clock.schedule_once(lambda dt: root.update()) + AddressFilter: + opacity: 1 + size_hint: 1, None + height: self.minimum_height + spacing: '5dp' + canvas.before: + Color: + rgba: 0.9, 0.9, 0.9, 1 + AddressButton: + id: change + text: root.message if root.message else _('Search') + on_release: Clock.schedule_once(lambda dt: app.description_dialog(addr_screen)) + ScrollView: + GridLayout: + cols: 1 + id: search_container + size_hint_y: None + height: self.minimum_height +''') + + +from electrum_gui.kivy.i18n import _ +from electrum_gui.kivy.uix.context_menu import ContextMenu + + +class AddressesDialog(Factory.Popup): + + def __init__(self, app, screen, callback): + Factory.Popup.__init__(self) + self.app = app + self.screen = screen + self.callback = callback + self.cards = {} + self.context_menu = None + + def get_card(self, addr, balance, is_used, label): + ci = self.cards.get(addr) + if ci is None: + ci = Factory.AddressItem() + ci.screen = self + ci.address = addr + self.cards[addr] = ci + + ci.memo = label + ci.amount = self.app.format_amount_and_units(balance) + request = self.app.wallet.get_payment_request(addr, self.app.electrum_config) + if is_used: + ci.status = _('Used') + else: + ci.status = _('Funded') if balance > 0 else _('Unused') + return ci + + + def update(self): + self.menu_actions = [(_('Use'), self.do_show), (_('Details'), self.do_view)] + wallet = self.app.wallet + if self.show_change == 0: + _list = wallet.get_receiving_addresses() + elif self.show_change == 1: + _list = wallet.get_change_addresses() + else: + _list = wallet.get_addresses() + search = self.screen.message + container = self.ids.search_container + container.clear_widgets() + n = 0 + for address in _list: + label = wallet.labels.get(address, '') + balance = sum(wallet.get_addr_balance(address)) + is_used = wallet.is_used(address) + if self.show_used == 1 and (balance or is_used): + continue + if self.show_used == 2 and balance == 0: + continue + if self.show_used == 3 and not is_used: + continue + card = self.get_card(address, balance, is_used, label) + if search and not self.ext_search(card, search): + continue + container.add_widget(card) + n += 1 + if not n: + msg = _('No address matching your search') + container.add_widget(EmptyLabel(text=msg)) + + def do_show(self, obj): + self.hide_menu() + self.dismiss() + self.app.show_request(obj.address) + + def do_view(self, obj): + req = self.app.wallet.get_payment_request(obj.address, self.app.electrum_config) + if req: + c, u, x = self.app.wallet.get_addr_balance(obj.address) + balance = c + u + x + if balance > 0: + req['fund'] = balance + status = req.get('status') + amount = req.get('amount') + address = req['address'] + if amount: + status = req.get('status') + status = request_text[status] + else: + received_amount = self.app.wallet.get_addr_received(address) + status = self.app.format_amount_and_units(received_amount) + self.app.show_pr_details(req, status, False) + + else: + req = { 'address': obj.address, 'status' : obj.status } + status = obj.status + c, u, x = self.app.wallet.get_addr_balance(obj.address) + balance = c + u + x + if balance > 0: + req['fund'] = balance + self.app.show_addr_details(req, status) + + def do_delete(self, obj): + from .dialogs.question import Question + def cb(result): + if result: + self.app.wallet.remove_payment_request(obj.address, self.app.electrum_config) + self.update() + d = Question(_('Delete request?'), cb) + d.open() + + def ext_search(self, card, search): + return card.memo.find(search) >= 0 or card.amount.find(search) >= 0 + + def show_menu(self, obj): + self.hide_menu() + self.context_menu = ContextMenu(obj, self.menu_actions) + self.ids.box.add_widget(self.context_menu) + + def hide_menu(self): + if self.context_menu is not None: + self.ids.box.remove_widget(self.context_menu) + self.context_menu = None diff --git a/gui/kivy/uix/dialogs/invoices.py b/gui/kivy/uix/dialogs/invoices.py new file mode 100644 index 000000000..5ba90f496 --- /dev/null +++ b/gui/kivy/uix/dialogs/invoices.py @@ -0,0 +1,172 @@ +from kivy.app import App +from kivy.factory import Factory +from kivy.properties import ObjectProperty +from kivy.lang import Builder +from decimal import Decimal + +Builder.load_string(''' + + #color: .305, .309, .309, 1 + text_size: self.width, None + halign: 'left' + valign: 'top' + + + requestor: '' + memo: '' + amount: '' + status: '' + date: '' + icon: 'atlas://gui/kivy/theming/light/important' + Image: + id: icon + source: root.icon + size_hint: None, 1 + width: self.height *.54 + mipmap: True + BoxLayout: + spacing: '8dp' + height: '32dp' + orientation: 'vertical' + Widget + InvoicesLabel: + text: root.requestor + shorten: True + Widget + InvoicesLabel: + text: root.memo + color: .699, .699, .699, 1 + font_size: '13sp' + shorten: True + Widget + BoxLayout: + spacing: '8dp' + height: '32dp' + orientation: 'vertical' + Widget + InvoicesLabel: + text: root.amount + font_size: '15sp' + halign: 'right' + width: '110sp' + Widget + InvoicesLabel: + text: root.status + font_size: '13sp' + halign: 'right' + color: .699, .699, .699, 1 + Widget + + + + id: popup + title: _('Invoices') + BoxLayout: + id: box + orientation: 'vertical' + spacing: '1dp' + ScrollView: + GridLayout: + cols: 1 + id: invoices_container + size_hint: 1, None + height: self.minimum_height + spacing: '2dp' + padding: '12dp' +''') + +from kivy.properties import BooleanProperty +from electrum_gui.kivy.i18n import _ +from electrum.util import format_time +from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED +from electrum_gui.kivy.uix.context_menu import ContextMenu + +invoice_text = { + PR_UNPAID:_('Pending'), + PR_UNKNOWN:_('Unknown'), + PR_PAID:_('Paid'), + PR_EXPIRED:_('Expired') +} +pr_icon = { + PR_UNPAID: 'atlas://gui/kivy/theming/light/important', + PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important', + PR_PAID: 'atlas://gui/kivy/theming/light/confirmed', + PR_EXPIRED: 'atlas://gui/kivy/theming/light/close' +} + + +class InvoicesDialog(Factory.Popup): + + def __init__(self, app, screen, callback): + Factory.Popup.__init__(self) + self.app = app + self.screen = screen + self.callback = callback + self.cards = {} + self.context_menu = None + + def get_card(self, pr): + key = pr.get_id() + ci = self.cards.get(key) + if ci is None: + ci = Factory.InvoiceItem() + ci.key = key + ci.screen = self + self.cards[key] = ci + ci.requestor = pr.get_requestor() + ci.memo = pr.get_memo() + amount = pr.get_amount() + if amount: + ci.amount = self.app.format_amount_and_units(amount) + status = self.app.wallet.invoices.get_status(ci.key) + ci.status = invoice_text[status] + ci.icon = pr_icon[status] + else: + ci.amount = _('No Amount') + ci.status = '' + exp = pr.get_expiration_date() + ci.date = format_time(exp) if exp else _('Never') + return ci + + def update(self): + self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)] + invoices_list = self.ids.invoices_container + invoices_list.clear_widgets() + _list = self.app.wallet.invoices.sorted_list() + for pr in _list: + ci = self.get_card(pr) + invoices_list.add_widget(ci) + if not _list: + msg = _('This screen shows the list of payment requests that have been sent to you. You may also use it to store contact addresses.') + invoices_list.add_widget(EmptyLabel(text=msg)) + + def do_pay(self, obj): + self.hide_menu() + self.dismiss() + pr = self.app.wallet.invoices.get(obj.key) + self.app.on_pr(pr) + + def do_view(self, obj): + pr = self.app.wallet.invoices.get(obj.key) + pr.verify(self.app.wallet.contacts) + self.app.show_pr_details(pr.get_dict(), obj.status, True) + + def do_delete(self, obj): + from .question import Question + def cb(result): + if result: + self.app.wallet.invoices.remove(obj.key) + self.hide_menu() + self.update() + d = Question(_('Delete invoice?'), cb) + d.open() + + def show_menu(self, obj): + self.hide_menu() + self.context_menu = ContextMenu(obj, self.menu_actions) + self.ids.box.add_widget(self.context_menu) + + def hide_menu(self): + if self.context_menu is not None: + self.ids.box.remove_widget(self.context_menu) + self.context_menu = None diff --git a/gui/kivy/uix/dialogs/requests.py b/gui/kivy/uix/dialogs/requests.py new file mode 100644 index 000000000..266a40436 --- /dev/null +++ b/gui/kivy/uix/dialogs/requests.py @@ -0,0 +1,157 @@ +from kivy.app import App +from kivy.factory import Factory +from kivy.properties import ObjectProperty +from kivy.lang import Builder +from decimal import Decimal + +Builder.load_string(''' + + #color: .305, .309, .309, 1 + text_size: self.width, None + halign: 'left' + valign: 'top' + + + address: '' + memo: '' + amount: '' + status: '' + date: '' + icon: 'atlas://gui/kivy/theming/light/important' + Image: + id: icon + source: root.icon + size_hint: None, 1 + width: self.height *.54 + mipmap: True + BoxLayout: + spacing: '8dp' + height: '32dp' + orientation: 'vertical' + Widget + RequestLabel: + text: root.address + shorten: True + Widget + RequestLabel: + text: root.memo + color: .699, .699, .699, 1 + font_size: '13sp' + shorten: True + Widget + BoxLayout: + spacing: '8dp' + height: '32dp' + orientation: 'vertical' + Widget + RequestLabel: + text: root.amount + halign: 'right' + font_size: '15sp' + Widget + RequestLabel: + text: root.status + halign: 'right' + font_size: '13sp' + color: .699, .699, .699, 1 + Widget + + + id: popup + title: _('Requests') + BoxLayout: + id:box + orientation: 'vertical' + spacing: '1dp' + ScrollView: + GridLayout: + cols: 1 + id: requests_container + size_hint: 1, None + height: self.minimum_height + spacing: '2dp' + padding: '12dp' +''') + +from kivy.properties import BooleanProperty +from electrum_gui.kivy.i18n import _ +from electrum.util import format_time +from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED +from electrum_gui.kivy.uix.context_menu import ContextMenu + +pr_icon = { + PR_UNPAID: 'atlas://gui/kivy/theming/light/important', + PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important', + PR_PAID: 'atlas://gui/kivy/theming/light/confirmed', + PR_EXPIRED: 'atlas://gui/kivy/theming/light/close' +} +request_text = { + PR_UNPAID: _('Pending'), + PR_UNKNOWN: _('Unknown'), + PR_PAID: _('Received'), + PR_EXPIRED: _('Expired') +} + + +class RequestsDialog(Factory.Popup): + + def __init__(self, app, screen, callback): + Factory.Popup.__init__(self) + self.app = app + self.screen = screen + self.callback = callback + self.cards = {} + self.context_menu = None + + def get_card(self, req): + address = req['address'] + ci = self.cards.get(address) + if ci is None: + ci = Factory.RequestItem() + ci.address = address + ci.screen = self + self.cards[address] = ci + + amount = req.get('amount') + ci.amount = self.app.format_amount_and_units(amount) if amount else '' + ci.memo = req.get('memo', '') + status, conf = self.app.wallet.get_request_status(address) + ci.status = request_text[status] + ci.icon = pr_icon[status] + #exp = pr.get_expiration_date() + #ci.date = format_time(exp) if exp else _('Never') + return ci + + def update(self): + self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.do_delete)] + requests_list = self.ids.requests_container + requests_list.clear_widgets() + _list = self.app.wallet.get_sorted_requests(self.app.electrum_config) + for pr in _list: + ci = self.get_card(pr) + requests_list.add_widget(ci) + + def do_show(self, obj): + self.hide_menu() + self.dismiss() + self.app.show_request(obj.address) + + def do_delete(self, req): + from .question import Question + def cb(result): + if result: + self.app.wallet.remove_payment_request(req.address, self.app.electrum_config) + self.hide_menu() + self.update() + d = Question(_('Delete request'), cb) + d.open() + + def show_menu(self, obj): + self.hide_menu() + self.context_menu = ContextMenu(obj, self.menu_actions) + self.ids.box.add_widget(self.context_menu) + + def hide_menu(self): + if self.context_menu is not None: + self.ids.box.remove_widget(self.context_menu) + self.context_menu = None diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py index 5664d439d..255e2d009 100644 --- a/gui/kivy/uix/screens.py +++ b/gui/kivy/uix/screens.py @@ -390,205 +390,10 @@ class ReceiveScreen(CScreen): addr = self.get_new_address() if not addr: self.app.show_info(_('Please use the existing requests first.')) - else: - self.save_request() - self.app.show_info(_('New request added to your list.')) - - -invoice_text = { - PR_UNPAID:_('Pending'), - PR_UNKNOWN:_('Unknown'), - PR_PAID:_('Paid'), - PR_EXPIRED:_('Expired') -} -request_text = { - PR_UNPAID: _('Pending'), - PR_UNKNOWN: _('Unknown'), - PR_PAID: _('Received'), - PR_EXPIRED: _('Expired') -} -pr_icon = { - PR_UNPAID: 'atlas://gui/kivy/theming/light/important', - PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important', - PR_PAID: 'atlas://gui/kivy/theming/light/confirmed', - PR_EXPIRED: 'atlas://gui/kivy/theming/light/close' -} - - -class InvoicesScreen(CScreen): - kvname = 'invoices' - cards = {} - - def get_card(self, pr): - key = pr.get_id() - ci = self.cards.get(key) - if ci is None: - ci = Factory.InvoiceItem() - ci.key = key - ci.screen = self - self.cards[key] = ci - - ci.requestor = pr.get_requestor() - ci.memo = pr.get_memo() - amount = pr.get_amount() - if amount: - ci.amount = self.app.format_amount_and_units(amount) - status = self.app.wallet.invoices.get_status(ci.key) - ci.status = invoice_text[status] - ci.icon = pr_icon[status] - else: - ci.amount = _('No Amount') - ci.status = '' - exp = pr.get_expiration_date() - ci.date = format_time(exp) if exp else _('Never') - return ci - - def update(self): - self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)] - invoices_list = self.screen.ids.invoices_container - invoices_list.clear_widgets() - _list = self.app.wallet.invoices.sorted_list() - for pr in _list: - ci = self.get_card(pr) - invoices_list.add_widget(ci) - if not _list: - msg = _('This screen shows the list of payment requests that have been sent to you. You may also use it to store contact addresses.') - invoices_list.add_widget(EmptyLabel(text=msg)) - - def do_pay(self, obj): - pr = self.app.wallet.invoices.get(obj.key) - self.app.on_pr(pr) - - def do_view(self, obj): - pr = self.app.wallet.invoices.get(obj.key) - pr.verify(self.app.wallet.contacts) - self.app.show_pr_details(pr.get_dict(), obj.status, True) - - def do_delete(self, obj): - from .dialogs.question import Question - def cb(result): - if result: - self.app.wallet.invoices.remove(obj.key) - self.app.update_tab('invoices') - d = Question(_('Delete invoice?'), cb) - d.open() - - -address_icon = { - 'Pending' : 'atlas://gui/kivy/theming/light/important', - 'Paid' : 'atlas://gui/kivy/theming/light/confirmed' -} - -class AddressScreen(CScreen): - kvname = 'address' - cards = {} - - def get_card(self, addr, balance, is_used, label): - ci = self.cards.get(addr) - if ci is None: - ci = Factory.AddressItem() - ci.screen = self - ci.address = addr - self.cards[addr] = ci - - ci.memo = label - ci.amount = self.app.format_amount_and_units(balance) - request = self.app.wallet.get_payment_request(addr, self.app.electrum_config) - if is_used: - ci.status = _('Used') - elif request: - status, conf = self.app.wallet.get_request_status(addr) - requested_amount = request.get('amount') - # make sure that requested amount is > 0 - if status == PR_PAID: - s = _('Request paid') - elif status == PR_UNPAID: - s = _('Request pending') - elif status == PR_EXPIRED: - s = _('Request expired') - else: - s = '' - ci.status = s + ': ' + self.app.format_amount_and_units(requested_amount) - else: - ci.status = _('Funded') if balance>0 else _('Unused') - return ci - - - def update(self): - self.menu_actions = [('Receive', self.do_show), ('Details', self.do_view)] - wallet = self.app.wallet - if self.screen.show_change == 0: - _list = wallet.get_receiving_addresses() - elif self.screen.show_change == 1: - _list = wallet.get_change_addresses() - else: - _list = wallet.get_addresses() - search = self.screen.message - container = self.screen.ids.search_container - container.clear_widgets() - n = 0 - for address in _list: - label = wallet.labels.get(address, '') - balance = sum(wallet.get_addr_balance(address)) - is_used = wallet.is_used(address) - if self.screen.show_used == 1 and (balance or is_used): - continue - if self.screen.show_used == 2 and balance == 0: - continue - if self.screen.show_used == 3 and not is_used: - continue - card = self.get_card(address, balance, is_used, label) - if search and not self.ext_search(card, search): - continue - container.add_widget(card) - n += 1 - if not n: - msg = _('No address matching your search') - container.add_widget(EmptyLabel(text=msg)) - - def do_show(self, obj): - self.app.show_request(obj.address) - - def do_view(self, obj): - req = self.app.wallet.get_payment_request(obj.address, self.app.electrum_config) - if req: - c, u, x = self.app.wallet.get_addr_balance(obj.address) - balance = c + u + x - if balance > 0: - req['fund'] = balance - status = req.get('status') - amount = req.get('amount') - address = req['address'] - if amount: - status = req.get('status') - status = request_text[status] - else: - received_amount = self.app.wallet.get_addr_received(address) - status = self.app.format_amount_and_units(received_amount) - self.app.show_pr_details(req, status, False) - - else: - req = { 'address': obj.address, 'status' : obj.status } - status = obj.status - c, u, x = self.app.wallet.get_addr_balance(obj.address) - balance = c + u + x - if balance > 0: - req['fund'] = balance - self.app.show_addr_details(req, status) - - def do_delete(self, obj): - from .dialogs.question import Question - def cb(result): - if result: - self.app.wallet.remove_payment_request(obj.address, self.app.electrum_config) - self.update() - d = Question(_('Delete request?'), cb) - d.open() - - def ext_search(self, card, search): - return card.memo.find(search) >= 0 or card.amount.find(search) >= 0 - + def do_save(self): + self.save_request() + self.app.show_info(_('Request was saved.')) class TabbedCarousel(Factory.TabbedPanel): diff --git a/gui/kivy/uix/ui_screens/address.kv b/gui/kivy/uix/ui_screens/address.kv deleted file mode 100644 index 07bb43677..000000000 --- a/gui/kivy/uix/ui_screens/address.kv +++ /dev/null @@ -1,90 +0,0 @@ -#:import _ electrum_gui.kivy.i18n._ -#:import Decimal decimal.Decimal -#:set btc_symbol chr(171) -#:set mbtc_symbol chr(187) -#:set font_light 'gui/kivy/data/fonts/Roboto-Condensed.ttf' - - - text_size: self.width, None - halign: 'left' - valign: 'top' - - - address: '' - memo: '' - amount: '' - status: '' - BoxLayout: - spacing: '8dp' - height: '32dp' - orientation: 'vertical' - Widget - AddressLabel: - text: root.address - shorten: True - Widget - AddressLabel: - text: (root.amount if root.status == 'Funded' else root.status) + ' ' + root.memo - color: .699, .699, .699, 1 - font_size: '13sp' - shorten: True - Widget - -AddressScreen: - id: addr_screen - name: 'address' - message: '' - pr_status: 'Pending' - show_change: 0 - show_used: 0 - on_message: - self.parent.update() - BoxLayout - padding: '12dp', '70dp', '12dp', '12dp' - spacing: '12dp' - orientation: 'vertical' - size_hint: 1, 1.1 - BoxLayout: - spacing: '6dp' - size_hint: 1, None - orientation: 'horizontal' - AddressFilter: - opacity: 1 - size_hint: 1, None - height: self.minimum_height - spacing: '5dp' - AddressButton: - id: search - text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change] - on_release: - root.show_change = (root.show_change + 1) % 3 - Clock.schedule_once(lambda dt: app.address_screen.update()) - AddressFilter: - opacity: 1 - size_hint: 1, None - height: self.minimum_height - spacing: '5dp' - AddressButton: - id: search - text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used] - on_release: - root.show_used = (root.show_used + 1) % 4 - Clock.schedule_once(lambda dt: app.address_screen.update()) - AddressFilter: - opacity: 1 - size_hint: 1, None - height: self.minimum_height - spacing: '5dp' - canvas.before: - Color: - rgba: 0.9, 0.9, 0.9, 1 - AddressButton: - id: change - text: root.message if root.message else _('Search') - on_release: Clock.schedule_once(lambda dt: app.description_dialog(addr_screen)) - ScrollView: - GridLayout: - cols: 1 - id: search_container - size_hint_y: None - height: self.minimum_height diff --git a/gui/kivy/uix/ui_screens/invoices.kv b/gui/kivy/uix/ui_screens/invoices.kv deleted file mode 100644 index 9621a8af6..000000000 --- a/gui/kivy/uix/ui_screens/invoices.kv +++ /dev/null @@ -1,66 +0,0 @@ - - #color: .305, .309, .309, 1 - text_size: self.width, None - halign: 'left' - valign: 'top' - - - requestor: '' - memo: '' - amount: '' - status: '' - date: '' - icon: 'atlas://gui/kivy/theming/light/important' - Image: - id: icon - source: root.icon - size_hint: None, 1 - width: self.height *.54 - mipmap: True - BoxLayout: - spacing: '8dp' - height: '32dp' - orientation: 'vertical' - Widget - InvoicesLabel: - text: root.requestor - shorten: True - Widget - InvoicesLabel: - text: root.memo - color: .699, .699, .699, 1 - font_size: '13sp' - shorten: True - Widget - BoxLayout: - spacing: '8dp' - height: '32dp' - orientation: 'vertical' - Widget - InvoicesLabel: - text: root.amount - font_size: '15sp' - halign: 'right' - width: '110sp' - Widget - InvoicesLabel: - text: root.status - font_size: '13sp' - halign: 'right' - color: .699, .699, .699, 1 - Widget - - -InvoicesScreen: - name: 'invoices' - BoxLayout: - orientation: 'vertical' - spacing: '1dp' - ScrollView: - GridLayout: - cols: 1 - id: invoices_container - size_hint: 1, None - height: self.minimum_height - spacing: '2dp' - padding: '12dp' diff --git a/gui/kivy/uix/ui_screens/receive.kv b/gui/kivy/uix/ui_screens/receive.kv index c58b77ec7..650be40ff 100644 --- a/gui/kivy/uix/ui_screens/receive.kv +++ b/gui/kivy/uix/ui_screens/receive.kv @@ -70,7 +70,7 @@ ReceiveScreen: id: address_label text: s.address if s.address else _('Bitcoin Address') shorten: True - disabled: True + on_release: Clock.schedule_once(lambda dt: app.addresses_dialog(s)) CardSeparator: opacity: message_selection.opacity color: blue_bottom.foreground_color @@ -110,16 +110,31 @@ ReceiveScreen: BoxLayout: size_hint: 1, None height: '48dp' + IconButton: + icon: 'atlas://gui/kivy/theming/light/save' + size_hint: 0.6, None + height: '48dp' + on_release: s.parent.do_save() Button: - text: _('Copy') + text: _('Requests') size_hint: 1, None height: '48dp' - on_release: s.parent.do_copy() + on_release: Clock.schedule_once(lambda dt: app.requests_dialog(s)) Button: - text: _('Share') + text: _('Copy') size_hint: 1, None height: '48dp' + on_release: s.parent.do_copy() + IconButton: + icon: 'atlas://gui/kivy/theming/light/share' + size_hint: 0.6, None + height: '48dp' on_release: s.parent.do_share() + BoxLayout: + size_hint: 1, None + height: '48dp' + Widget + size_hint: 2, 1 Button: text: _('New') size_hint: 1, None diff --git a/gui/kivy/uix/ui_screens/requests.kv b/gui/kivy/uix/ui_screens/requests.kv deleted file mode 100644 index 1e39ec7db..000000000 --- a/gui/kivy/uix/ui_screens/requests.kv +++ /dev/null @@ -1,66 +0,0 @@ - - #color: .305, .309, .309, 1 - text_size: self.width, None - halign: 'left' - valign: 'top' - - - address: '' - memo: '' - amount: '' - status: '' - date: '' - icon: 'atlas://gui/kivy/theming/light/important' - Image: - id: icon - source: root.icon - size_hint: None, 1 - width: self.height *.54 - mipmap: True - BoxLayout: - spacing: '8dp' - height: '32dp' - orientation: 'vertical' - Widget - RequestLabel: - text: root.address - shorten: True - Widget - RequestLabel: - text: root.memo - color: .699, .699, .699, 1 - font_size: '13sp' - shorten: True - Widget - BoxLayout: - spacing: '8dp' - height: '32dp' - orientation: 'vertical' - Widget - RequestLabel: - text: root.amount - halign: 'right' - font_size: '15sp' - Widget - RequestLabel: - text: root.status - halign: 'right' - font_size: '13sp' - color: .699, .699, .699, 1 - Widget - - - -RequestsScreen: - name: 'requests' - BoxLayout: - orientation: 'vertical' - spacing: '1dp' - ScrollView: - GridLayout: - cols: 1 - id: requests_container - size_hint_y: None - height: self.minimum_height - spacing: '2dp' - padding: '12dp' diff --git a/gui/kivy/uix/ui_screens/send.kv b/gui/kivy/uix/ui_screens/send.kv index f2a361e37..7269cb9b6 100644 --- a/gui/kivy/uix/ui_screens/send.kv +++ b/gui/kivy/uix/ui_screens/send.kv @@ -34,6 +34,7 @@ SendScreen: text: s.address if s.address else _('Recipient') shorten: True on_release: Clock.schedule_once(lambda dt: app.show_info(_('Copy and paste the recipient address using the Paste button, or use the camera to scan a QR code.'))) + #on_release: Clock.schedule_once(lambda dt: app.popup_dialog('contacts')) CardSeparator: opacity: int(not root.is_pr) color: blue_bottom.foreground_color @@ -93,25 +94,29 @@ SendScreen: size_hint: 1, None height: '48dp' IconButton: - id: qr size_hint: 0.6, 1 - on_release: Clock.schedule_once(lambda dt: app.scan_qr(on_complete=app.on_qr)) - icon: 'atlas://gui/kivy/theming/light/camera' + on_release: s.parent.do_save() + icon: 'atlas://gui/kivy/theming/light/save' + Button: + text: _('Invoices') + size_hint: 1, 1 + on_release: Clock.schedule_once(lambda dt: app.invoices_dialog(s)) Button: text: _('Paste') on_release: s.parent.do_paste() - Button: - text: _('Clear') - on_release: s.parent.do_clear() IconButton: + id: qr size_hint: 0.6, 1 - on_release: s.parent.do_save() - icon: 'atlas://gui/kivy/theming/light/save' + on_release: Clock.schedule_once(lambda dt: app.scan_qr(on_complete=app.on_qr)) + icon: 'atlas://gui/kivy/theming/light/camera' BoxLayout: size_hint: 1, None height: '48dp' + Button: + text: _('Clear') + on_release: s.parent.do_clear() Widget: - size_hint: 2, 1 + size_hint: 1, 1 Button: text: _('Pay') size_hint: 1, 1 From a6e23ae275da6dddb6ed93b5a895d6b119e6fd8d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 6 Mar 2018 15:13:19 +0100 Subject: [PATCH 2/4] share icon file --- gui/kivy/theming/light/share.png | Bin 0 -> 3325 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gui/kivy/theming/light/share.png diff --git a/gui/kivy/theming/light/share.png b/gui/kivy/theming/light/share.png new file mode 100644 index 0000000000000000000000000000000000000000..d0dc761d4544fc091d2e59a22109fc60ffaa0a61 GIT binary patch literal 3325 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 Date: Tue, 6 Mar 2018 18:07:38 +0100 Subject: [PATCH 3/4] fix #4050 --- gui/kivy/uix/dialogs/invoices.py | 3 --- gui/kivy/uix/screens.py | 2 -- 2 files changed, 5 deletions(-) diff --git a/gui/kivy/uix/dialogs/invoices.py b/gui/kivy/uix/dialogs/invoices.py index 5ba90f496..d8e4f884d 100644 --- a/gui/kivy/uix/dialogs/invoices.py +++ b/gui/kivy/uix/dialogs/invoices.py @@ -136,9 +136,6 @@ class InvoicesDialog(Factory.Popup): for pr in _list: ci = self.get_card(pr) invoices_list.add_widget(ci) - if not _list: - msg = _('This screen shows the list of payment requests that have been sent to you. You may also use it to store contact addresses.') - invoices_list.add_widget(EmptyLabel(text=msg)) def do_pay(self, obj): self.hide_menu() diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py index 255e2d009..d8e283362 100644 --- a/gui/kivy/uix/screens.py +++ b/gui/kivy/uix/screens.py @@ -27,8 +27,6 @@ from .context_menu import ContextMenu from electrum_gui.kivy.i18n import _ -class EmptyLabel(Factory.Label): - pass class CScreen(Factory.Screen): __events__ = ('on_activate', 'on_deactivate', 'on_enter', 'on_leave') From 971a6979ee04055482371f30647b4ad9c886c3df Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 6 Mar 2018 19:36:57 +0100 Subject: [PATCH 4/4] kivy: do not open invoices, requests dialogs if list is empty --- gui/kivy/main_window.py | 10 ++++++++++ gui/kivy/uix/screens.py | 1 - 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py index c719907c8..be9409d65 100644 --- a/gui/kivy/main_window.py +++ b/gui/kivy/main_window.py @@ -837,12 +837,22 @@ class ElectrumWindow(App): def invoices_dialog(self, screen): from .uix.dialogs.invoices import InvoicesDialog + if len(self.wallet.invoices.sorted_list()) == 0: + self.show_info(' '.join([ + _('No saved invoices.'), + _('Signed invoices are saved automatically when you scan them.'), + _('You may also save unsigned requests or contact addresses using the save button.') + ])) + return popup = InvoicesDialog(self, screen, None) popup.update() popup.open() def requests_dialog(self, screen): from .uix.dialogs.requests import RequestsDialog + if len(self.wallet.get_sorted_requests(self.electrum_config)) == 0: + self.show_info(_('No saved requests.')) + return popup = RequestsDialog(self, screen, None) popup.update() popup.open() diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py index d8e283362..a4cd31442 100644 --- a/gui/kivy/uix/screens.py +++ b/gui/kivy/uix/screens.py @@ -217,7 +217,6 @@ class SendScreen(CScreen): pr = make_unsigned_request(req).SerializeToString() pr = PaymentRequest(pr) self.app.wallet.invoices.add(pr) - self.app.update_tab('invoices') self.app.show_info(_("Invoice saved")) if pr.is_pr(): self.screen.is_pr = True