From 9ba5a3480080c9e94a99c231b458a810a2abf572 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 29 Mar 2022 18:38:04 +0200 Subject: [PATCH] Qt receive tab: - show payment options in tabs: URI, Address or Lightning - use vertical tabs to save space - switch between QR and text views - open standalone QR window through menu, instead of clicking on QR code --- electrum/gui/icons/link.png | Bin 0 -> 23125 bytes electrum/gui/qt/main_window.py | 171 ++++++++++++++++++++++---------- electrum/gui/qt/request_list.py | 32 ++---- electrum/gui/qt/util.py | 41 ++++++++ 4 files changed, 168 insertions(+), 76 deletions(-) create mode 100644 electrum/gui/icons/link.png diff --git a/electrum/gui/icons/link.png b/electrum/gui/icons/link.png new file mode 100644 index 0000000000000000000000000000000000000000..af462bc202f3d1fbee51ba54fbb3d6ed19b26cee GIT binary patch literal 23125 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 literal 0 HcmV?d00001 diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index af725106c..c99bd830c 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -778,6 +778,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): tools_menu.addSeparator() paytomany_menu = tools_menu.addAction(_("&Pay to many"), self.paytomany) + tools_menu.addAction(_("&Show QR code in separate window"), self.toggle_receive_qr_window) raw_transaction_menu = tools_menu.addMenu(_("&Load transaction")) raw_transaction_menu.addAction(_("&From file"), self.do_process_from_file) @@ -1092,6 +1093,20 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): d = LightningTxDialog(self, tx_item) d.show() + def toggle_receive_qr(self, toggle=False): + b = not self.config.get('receive_qr_visible', False) + self.config.set_key('receive_qr_visible', b) + self.update_receive_widgets() + + def update_receive_widgets(self): + b = self.config.get('receive_qr_visible', False) + self.receive_address_e.setVisible(b) + self.receive_address_qr.setVisible(not b) + self.receive_URI_e.setVisible(b) + self.receive_URI_qr.setVisible(not b) + self.receive_lightning_e.setVisible(b) + self.receive_lightning_qr.setVisible(not b) + def create_receive_tab(self): # A 4-column grid layout. All the stretch is in the last column. # The exchange rate plugin adds a fiat widget in column 2 @@ -1099,15 +1114,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): grid.setSpacing(8) grid.setColumnStretch(3, 1) - self.receive_message_e = SizedFreezableLineEdit(width=700) + self.receive_message_e = SizedFreezableLineEdit(width=400) grid.addWidget(QLabel(_('Description')), 0, 0) grid.addWidget(self.receive_message_e, 0, 1, 1, 4) - self.receive_message_e.textChanged.connect(self.update_receive_qr) self.receive_amount_e = BTCAmountEdit(self.get_decimal_point) grid.addWidget(QLabel(_('Requested amount')), 1, 0) grid.addWidget(self.receive_amount_e, 1, 1) - self.receive_amount_e.textChanged.connect(self.update_receive_qr) self.fiat_receive_e = AmountEdit(self.fx.get_currency if self.fx else '') if not self.fx or not self.fx.is_enabled(): @@ -1159,48 +1172,67 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): buttons.addWidget(self.create_invoice_button) grid.addLayout(buttons, 4, 0, 1, -1) - self.receive_payreq_e = ButtonsTextEdit() - self.receive_payreq_e.setFont(QFont(MONOSPACE_FONT)) - self.receive_payreq_e.addCopyButton(self.app) - self.receive_payreq_e.setReadOnly(True) - self.receive_payreq_e.textChanged.connect(self.update_receive_qr) - self.receive_payreq_e.setFocusPolicy(Qt.ClickFocus) - - self.receive_qr = QRCodeWidget(fixedSize=220) - self.receive_qr.mouseReleaseEvent = lambda x: self.toggle_qr_window() - self.receive_qr.enterEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.PointingHandCursor)) - self.receive_qr.leaveEvent = lambda x: self.app.setOverrideCursor(QCursor(Qt.ArrowCursor)) - self.receive_address_e = ButtonsTextEdit() - self.receive_address_e.setFont(QFont(MONOSPACE_FONT)) - self.receive_address_e.addCopyButton(self.app) - self.receive_address_e.setReadOnly(True) - self.receive_address_e.textChanged.connect(self.update_receive_address_styling) + self.receive_URI_e = ButtonsTextEdit() + self.receive_lightning_e = ButtonsTextEdit() + #self.receive_URI_e.setFocusPolicy(Qt.ClickFocus) - qr_show = lambda: self.show_qrcode(str(self.receive_address_e.text()), _('Receiving address'), parent=self) + fixedSize = 200 qr_icon = "qrcode_white.png" if ColorScheme.dark_scheme else "qrcode.png" - self.receive_address_e.addButton(qr_icon, qr_show, _("Show as QR code")) + for e in [self.receive_address_e, self.receive_URI_e, self.receive_lightning_e]: + e.setFont(QFont(MONOSPACE_FONT)) + e.addCopyButton(self.app) + e.setReadOnly(True) + e.setFixedSize(fixedSize, fixedSize) + e.addButton(qr_icon, self.toggle_receive_qr, _("Show as QR code")) + + self.receive_address_qr = QRCodeWidget(fixedSize=fixedSize) + self.receive_URI_qr = QRCodeWidget(fixedSize=fixedSize) + self.receive_lightning_qr = QRCodeWidget(fixedSize=fixedSize) + + self.receive_lightning_e.textChanged.connect(self.update_receive_widgets) + + receive_address_layout = QHBoxLayout() + receive_address_layout.addWidget(self.receive_address_e) + receive_address_layout.addWidget(self.receive_address_qr) + receive_URI_layout = QHBoxLayout() + receive_URI_layout.addWidget(self.receive_URI_e) + receive_URI_layout.addWidget(self.receive_URI_qr) + receive_lightning_layout = QHBoxLayout() + receive_lightning_layout.addWidget(self.receive_lightning_e) + receive_lightning_layout.addWidget(self.receive_lightning_qr) + + from .util import VTabWidget + self.receive_tabs = VTabWidget() + receive_address_widget = QWidget() + receive_address_widget.setLayout(receive_address_layout) + receive_URI_widget = QWidget() + receive_URI_widget.setLayout(receive_URI_layout) + receive_lightning_widget = QWidget() + receive_lightning_widget.setLayout(receive_lightning_layout) + + self.receive_tabs.addTab(receive_URI_widget, read_QIcon("link.png"), _('URI')) + self.receive_tabs.addTab(receive_address_widget, read_QIcon("bitcoin.png"), _('Address')) + self.receive_tabs.addTab(receive_lightning_widget, read_QIcon("lightning.png"), _('Lightning')) + def on_current_changed(index): + self.update_receive_qr_window() + def on_tab_bar_clicked(index): + w = self.receive_tabs.widget(index) + if w == self.receive_tabs.currentWidget() and self.receive_tabs.isTabEnabled(index): + self.toggle_receive_qr() + self.receive_tabs.tabBarClicked.connect(on_tab_bar_clicked) + self.receive_tabs.currentChanged.connect(on_current_changed) + self.receive_tabs.setCurrentIndex(self.config.get('receive_tabs_index', 0)) + self.receive_tabs.currentChanged.connect(lambda i: self.config.set_key('receive_tabs_index', i)) + receive_tabs_sp = self.receive_tabs.sizePolicy() + receive_tabs_sp.setRetainSizeWhenHidden(True) + self.receive_tabs.setSizePolicy(receive_tabs_sp) + self.receive_tabs.setVisible(False) self.receive_requests_label = QLabel(_('Receive queue')) - from .request_list import RequestList self.request_list = RequestList(self) - receive_tabs = QTabWidget() - receive_tabs.addTab(self.receive_address_e, _('Address')) - receive_tabs.addTab(self.receive_payreq_e, _('Request')) - receive_tabs.addTab(self.receive_qr, _('QR Code')) - receive_tabs.setCurrentIndex(self.config.get('receive_tabs_index', 0)) - receive_tabs.currentChanged.connect(lambda i: self.config.set_key('receive_tabs_index', i)) - receive_tabs_sp = receive_tabs.sizePolicy() - receive_tabs_sp.setRetainSizeWhenHidden(True) - receive_tabs.setSizePolicy(receive_tabs_sp) - - def maybe_hide_receive_tabs(): - receive_tabs.setVisible(bool(self.receive_payreq_e.text())) - self.receive_payreq_e.textChanged.connect(maybe_hide_receive_tabs) - maybe_hide_receive_tabs() - # layout vbox_g = QVBoxLayout() vbox_g.addLayout(grid) @@ -1208,7 +1240,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): hbox = QHBoxLayout() hbox.addLayout(vbox_g) hbox.addStretch() - hbox.addWidget(receive_tabs) + hbox.addWidget(self.receive_tabs) w = QWidget() w.searchable_list = self.request_list @@ -1222,6 +1254,43 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): return w + def show_receive_request(self, req): + addr = req.get_address() or '' + URI = req.get_bip21_URI() if addr else '' + lnaddr = req.lightning_invoice or '' + can_receive_lightning = self.wallet.lnworker and req.get_amount_sat() <= self.wallet.lnworker.num_sats_can_receive() + if not can_receive_lightning: + lnaddr = '' + icon_name = "lightning.png" if can_receive_lightning else "lightning_disconnected.png" + self.receive_tabs.setTabIcon(2, read_QIcon(icon_name)) + # encode lightning invoices as uppercase so QR encoding can use + # alphanumeric mode; resulting in smaller QR codes + lnaddr_qr = lnaddr.upper() + self.receive_address_e.setText(addr) + self.receive_address_qr.setData(addr) + self.receive_URI_e.setText(URI) + self.receive_URI_qr.setData(URI) + self.receive_lightning_e.setText(lnaddr) # TODO maybe prepend "lightning:" ?? + self.receive_lightning_qr.setData(lnaddr_qr) + # macOS hack (similar to #4777) + self.receive_lightning_e.repaint() + self.receive_URI_e.repaint() + self.receive_address_e.repaint() + # always show + self.receive_tabs.setVisible(True) + self.update_receive_qr_window() + + def update_receive_qr_window(self): + if self.qr_window and self.qr_window.isVisible(): + i = self.receive_tabs.currentIndex() + if i == 0: + data = self.receive_address_qr.data + elif i == 1: + data = self.receive_URI_qr.data + else: + data = self.receive_lightning_qr.data + self.qr_window.qrw.setData(data) + def delete_requests(self, keys): for key in keys: self.wallet.delete_request(key) @@ -1276,7 +1345,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): except InvoiceError as e: self.show_error(_('Error creating payment request') + ':\n' + str(e)) return - + except Exception as e: + self.logger.exception('Error adding payment request') + self.show_error(_('Error adding payment request') + ':\n' + repr(e)) + return + self.sign_payment_request(address) assert key is not None self.address_list.refresh_all() self.request_list.update() @@ -1290,7 +1363,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): title = _('Invoice') if r.is_lightning() else _('Address') self.do_copy(content, title=title) - def get_bitcoin_address_for_request(self, amount: int) -> Optional[str]: + def get_bitcoin_address_for_request(self, amount) -> Optional[str]: addr = self.wallet.get_unused_address() if addr is None: if not self.wallet.is_deterministic(): # imported wallet @@ -1318,15 +1391,17 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): QToolTip.showText(QCursor.pos(), tooltip_text, self) def clear_receive_tab(self): - self.receive_payreq_e.setText('') self.receive_address_e.setText('') + self.receive_URI_e.setText('') + self.receive_lightning_e.setText('') + self.receive_tabs.setVisible(False) self.receive_message_e.setText('') self.receive_amount_e.setAmount(None) self.expires_label.hide() self.expires_combo.show() self.request_list.clearSelection() - def toggle_qr_window(self): + def toggle_receive_qr_window(self): from . import qrwindow if not self.qr_window: self.qr_window = qrwindow.QR_Window(self) @@ -1339,7 +1414,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): else: self.qr_window_geometry = self.qr_window.geometry() self.qr_window.setVisible(False) - self.update_receive_qr() def show_send_tab(self): self.tabs.setCurrentIndex(self.tabs.indexOf(self.send_tab)) @@ -1347,16 +1421,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): def show_receive_tab(self): self.tabs.setCurrentIndex(self.tabs.indexOf(self.receive_tab)) - def update_receive_qr(self): - uri = str(self.receive_payreq_e.text()) - if maybe_extract_bolt11_invoice(uri): - # encode lightning invoices as uppercase so QR encoding can use - # alphanumeric mode; resulting in smaller QR codes - uri = uri.upper() - self.receive_qr.setData(uri) - if self.qr_window and self.qr_window.isVisible(): - self.qr_window.qrw.setData(uri) - def update_receive_address_styling(self): addr = str(self.receive_address_e.text()) if is_address(addr) and self.wallet.is_used(addr): @@ -1624,6 +1688,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.need_update.set() def on_payment_failed(self, wallet, key, reason): + invoice = self.wallet.get_invoice(key) self.show_error(_('Payment failed') + '\n\n' + reason) def read_invoice(self): diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py index 082995445..ad0fbc6c5 100644 --- a/electrum/gui/qt/request_list.py +++ b/electrum/gui/qt/request_list.py @@ -86,7 +86,8 @@ class RequestList(MyTreeView): def item_changed(self, idx: Optional[QModelIndex]): if idx is None: - self.parent.receive_payreq_e.setText('') + self.parent.receive_URI_e.setText('') + self.parent.receive_lightning_e.setText('') self.parent.receive_address_e.setText('') return if not idx.isValid(): @@ -98,14 +99,7 @@ class RequestList(MyTreeView): if req is None: self.update() return - if req.is_lightning(): - self.parent.receive_payreq_e.setText(req.lightning_invoice) # TODO maybe prepend "lightning:" ?? - self.parent.receive_address_e.setText(req.lightning_invoice) - else: - self.parent.receive_payreq_e.setText(self.parent.wallet.get_request_URI(req)) - self.parent.receive_address_e.setText(req.get_address()) - self.parent.receive_payreq_e.repaint() # macOS hack (similar to #4777) - self.parent.receive_address_e.repaint() # macOS hack (similar to #4777) + self.parent.show_receive_request(req) def clearSelection(self): super().clearSelection() @@ -138,20 +132,12 @@ class RequestList(MyTreeView): date = format_time(timestamp) amount_str = self.parent.format_amount(amount) if amount else "" labels = [date, message, amount_str, status_str] - if req.is_lightning(): - icon = read_QIcon("lightning.png") - tooltip = 'lightning request' - else: - icon = read_QIcon("bitcoin.png") - tooltip = 'onchain request' items = [QStandardItem(e) for e in labels] self.set_editability(items) #items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE) items[self.Columns.DATE].setData(key, ROLE_KEY) items[self.Columns.DATE].setData(timestamp, ROLE_SORT_ORDER) - items[self.Columns.DATE].setIcon(icon) items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status))) - items[self.Columns.DATE].setToolTip(tooltip) self.std_model.insertRow(self.std_model.rowCount(), items) self.filter() self.proxy.setDynamicSortFilter(True) @@ -186,13 +172,13 @@ class RequestList(MyTreeView): self.update() return menu = QMenu(self) - self.add_copy_menu(menu, idx) - if req.is_lightning(): - menu.addAction(_("Copy Request"), lambda: self.parent.do_copy(req.lightning_invoice, title='Lightning Request')) - else: - URI = self.wallet.get_request_URI(req) - menu.addAction(_("Copy Request"), lambda: self.parent.do_copy(URI, title='Bitcoin URI')) + if req.get_address(): menu.addAction(_("Copy Address"), lambda: self.parent.do_copy(req.get_address(), title='Bitcoin Address')) + URI = self.wallet.get_request_URI(req) + menu.addAction(_("Copy URI"), lambda: self.parent.do_copy(URI, title='Bitcoin URI')) + if req.is_lightning(): + menu.addAction(_("Copy Lightning Request"), lambda: self.parent.do_copy(req.lightning_invoice, title='Lightning Request')) + self.add_copy_menu(menu, idx) #if 'view_url' in req: # menu.addAction(_("View in web browser"), lambda: webopen(req['view_url'])) menu.addAction(_("Delete"), lambda: self.parent.delete_requests([key])) diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py index d9158ecc3..9376eb421 100644 --- a/electrum/gui/qt/util.py +++ b/electrum/gui/qt/util.py @@ -1301,6 +1301,47 @@ class ImageGraphicsEffect(QObject): return result +# vertical tabs +# from https://stackoverflow.com/questions/51230544/pyqt5-how-to-set-tabwidget-west-but-keep-the-text-horizontal +from PyQt5 import QtWidgets, QtCore + +class VTabBar(QtWidgets.QTabBar): + + def tabSizeHint(self, index): + s = QtWidgets.QTabBar.tabSizeHint(self, index) + s.transpose() + return s + + def paintEvent(self, event): + painter = QtWidgets.QStylePainter(self) + opt = QtWidgets.QStyleOptionTab() + + for i in range(self.count()): + self.initStyleOption(opt, i) + painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, opt) + painter.save() + + s = opt.rect.size() + s.transpose() + r = QtCore.QRect(QtCore.QPoint(), s) + r.moveCenter(opt.rect.center()) + opt.rect = r + + c = self.tabRect(i).center() + painter.translate(c) + painter.rotate(90) + painter.translate(-c) + painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, opt); + painter.restore() + + +class VTabWidget(QtWidgets.QTabWidget): + def __init__(self, *args, **kwargs): + QtWidgets.QTabWidget.__init__(self, *args, **kwargs) + self.setTabBar(VTabBar(self)) + self.setTabPosition(QtWidgets.QTabWidget.West) + + if __name__ == "__main__": app = QApplication([]) t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done"))