Browse Source

connect fee edits together

3.1
SomberNight 7 years ago
committed by ThomasV
parent
commit
3ba864bbca
  1. 17
      gui/qt/amountedit.py
  2. 136
      gui/qt/main_window.py
  3. 6
      lib/simple_config.py
  4. 10
      lib/wallet.py

17
gui/qt/amountedit.py

@ -81,7 +81,6 @@ class BTCAmountEdit(AmountEdit):
def _base_unit(self): def _base_unit(self):
p = self.decimal_point() p = self.decimal_point()
assert p in [2, 5, 8]
if p == 8: if p == 8:
return 'BTC' return 'BTC'
if p == 5: if p == 5:
@ -104,6 +103,18 @@ class BTCAmountEdit(AmountEdit):
else: else:
self.setText(format_satoshis_plain(amount, self.decimal_point())) self.setText(format_satoshis_plain(amount, self.decimal_point()))
class BTCkBEdit(BTCAmountEdit):
class FeerateEdit(BTCAmountEdit):
def _base_unit(self): def _base_unit(self):
return BTCAmountEdit._base_unit(self) + '/kB' p = self.decimal_point()
if p == 2:
return 'mBTC/kB'
if p == 0:
return 'sat/byte'
raise Exception('Unknown base unit')
def get_amount(self):
sat_per_byte_amount = BTCAmountEdit.get_amount(self)
if sat_per_byte_amount is None:
return None
return 1000 * sat_per_byte_amount

136
gui/qt/main_window.py

@ -38,7 +38,7 @@ from PyQt5.QtWidgets import *
from electrum.util import bh2u, bfh from electrum.util import bh2u, bfh
from electrum import keystore from electrum import keystore, simple_config
from electrum.bitcoin import COIN, is_address, TYPE_ADDRESS, NetworkConstants from electrum.bitcoin import COIN, is_address, TYPE_ADDRESS, NetworkConstants
from electrum.plugins import run_hook from electrum.plugins import run_hook
from electrum.i18n import _ from electrum.i18n import _
@ -54,7 +54,7 @@ try:
except: except:
plot_history = None plot_history = None
from .amountedit import AmountEdit, BTCAmountEdit, MyLineEdit from .amountedit import AmountEdit, BTCAmountEdit, MyLineEdit, FeerateEdit
from .qrcodewidget import QRCodeWidget, QRDialog from .qrcodewidget import QRCodeWidget, QRDialog
from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit
from .transaction_dialog import show_transaction from .transaction_dialog import show_transaction
@ -1073,19 +1073,30 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.fee_slider = FeeSlider(self, self.config, fee_cb) self.fee_slider = FeeSlider(self, self.config, fee_cb)
self.fee_slider.setFixedWidth(140) self.fee_slider.setFixedWidth(140)
def on_fee_or_feerate(edit_changed, editing_finished):
edit_other = self.feerate_e if edit_changed == self.fee_e else self.fee_e
if editing_finished:
if not edit_changed.get_amount():
# This is so that when the user blanks the fee and moves on,
# we go back to auto-calculate mode and put a fee back.
edit_changed.setModified(False)
else:
# edit_changed was edited just now, so make sure we will
# freeze the correct fee setting (this)
edit_other.setModified(False)
self.update_fee()
self.size_e = AmountEdit(lambda: 'bytes') self.size_e = AmountEdit(lambda: 'bytes')
self.size_e.setReadOnly(True) self.size_e.setFrozen(True)
self.feerate_e = AmountEdit(lambda: self.base_unit() + '/kB' if self.fee_unit else 'sat/bytes')
self.feerate_e.textEdited.connect(self.update_fee) self.feerate_e = FeerateEdit(lambda: 2 if self.fee_unit else 0)
self.feerate_e.textEdited.connect(partial(on_fee_or_feerate, self.feerate_e, False))
self.feerate_e.editingFinished.connect(partial(on_fee_or_feerate, self.feerate_e, True))
self.fee_e = BTCAmountEdit(self.get_decimal_point) self.fee_e = BTCAmountEdit(self.get_decimal_point)
self.fee_e.textEdited.connect(partial(on_fee_or_feerate, self.fee_e, False))
self.fee_e.editingFinished.connect(partial(on_fee_or_feerate, self.fee_e, True))
if not self.config.get('show_fee', False):
self.fee_e.setVisible(False)
self.fee_e.textEdited.connect(self.update_fee)
# This is so that when the user blanks the fee and moves on,
# we go back to auto-calculate mode and put a fee back.
self.fee_e.editingFinished.connect(self.update_fee)
self.connect_fields(self, self.amount_e, self.fiat_send_e, self.fee_e) self.connect_fields(self, self.amount_e, self.fiat_send_e, self.fee_e)
#self.rbf_checkbox = QCheckBox(_('Replaceable')) #self.rbf_checkbox = QCheckBox(_('Replaceable'))
@ -1095,13 +1106,28 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
#self.rbf_checkbox.setToolTip('<p>' + ' '.join(msg) + '</p>') #self.rbf_checkbox.setToolTip('<p>' + ' '.join(msg) + '</p>')
#self.rbf_checkbox.setVisible(False) #self.rbf_checkbox.setVisible(False)
grid.addWidget(self.fee_e_label, 5, 0) vbox_feelabel = QVBoxLayout()
grid.addWidget(self.feerate_e, 5, 1) vbox_feelabel.addWidget(self.fee_e_label)
grid.addWidget(self.size_e, 5, 2) vbox_feelabel.addStretch(1)
grid.addWidget(self.fee_e, 5, 3) grid.addLayout(vbox_feelabel, 5, 0)
self.fee_adv_controls = QWidget()
hbox = QHBoxLayout(self.fee_adv_controls)
hbox.setContentsMargins(0, 0, 0, 0)
hbox.addWidget(self.feerate_e)
hbox.addWidget(self.size_e)
hbox.addWidget(self.fee_e)
grid.addWidget(self.fee_slider, 6, 1) self.size_e.setStyleSheet(ColorScheme.BLUE.as_stylesheet())
#grid.addWidget(self.rbf_checkbox, 5, 3)
vbox_feecontrol = QVBoxLayout()
vbox_feecontrol.addWidget(self.fee_adv_controls)
vbox_feecontrol.addWidget(self.fee_slider)
grid.addLayout(vbox_feecontrol, 5, 1, 1, 3)
if not self.config.get('show_fee', False):
self.fee_adv_controls.setVisible(False)
self.preview_button = EnterButton(_("Preview"), self.do_preview) self.preview_button = EnterButton(_("Preview"), self.do_preview)
self.preview_button.setToolTip(_('Display the details of your transactions before signing it.')) self.preview_button.setToolTip(_('Display the details of your transactions before signing it.'))
@ -1112,7 +1138,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
buttons.addWidget(self.clear_button) buttons.addWidget(self.clear_button)
buttons.addWidget(self.preview_button) buttons.addWidget(self.preview_button)
buttons.addWidget(self.send_button) buttons.addWidget(self.send_button)
grid.addLayout(buttons, 7, 1, 1, 3) grid.addLayout(buttons, 6, 1, 1, 3)
self.amount_e.shortcut.connect(self.spend_max) self.amount_e.shortcut.connect(self.spend_max)
self.payto_e.textChanged.connect(self.update_fee) self.payto_e.textChanged.connect(self.update_fee)
@ -1126,26 +1152,40 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def entry_changed(): def entry_changed():
text = "" text = ""
amt_color = ColorScheme.DEFAULT
fee_color = ColorScheme.DEFAULT
feerate_color = ColorScheme.DEFAULT
if self.not_enough_funds: if self.not_enough_funds:
amt_color, fee_color = ColorScheme.RED, ColorScheme.RED amt_color, fee_color = ColorScheme.RED, ColorScheme.RED
feerate_color = ColorScheme.RED
text = _( "Not enough funds" ) text = _( "Not enough funds" )
c, u, x = self.wallet.get_frozen_balance() c, u, x = self.wallet.get_frozen_balance()
if c+u+x: if c+u+x:
text += ' (' + self.format_amount(c+u+x).strip() + ' ' + self.base_unit() + ' ' +_("are frozen") + ')' text += ' (' + self.format_amount(c+u+x).strip() + ' ' + self.base_unit() + ' ' +_("are frozen") + ')'
# blue color denotes auto-filled values
elif self.fee_e.isModified(): elif self.fee_e.isModified():
amt_color, fee_color = ColorScheme.DEFAULT, ColorScheme.DEFAULT feerate_color = ColorScheme.BLUE
elif self.feerate_e.isModified():
fee_color = ColorScheme.BLUE
elif self.amount_e.isModified(): elif self.amount_e.isModified():
amt_color, fee_color = ColorScheme.DEFAULT, ColorScheme.BLUE fee_color = ColorScheme.BLUE
feerate_color = ColorScheme.BLUE
else: else:
amt_color, fee_color = ColorScheme.BLUE, ColorScheme.BLUE amt_color = ColorScheme.BLUE
fee_color = ColorScheme.BLUE
feerate_color = ColorScheme.BLUE
self.statusBar().showMessage(text) self.statusBar().showMessage(text)
self.amount_e.setStyleSheet(amt_color.as_stylesheet()) self.amount_e.setStyleSheet(amt_color.as_stylesheet())
self.fee_e.setStyleSheet(fee_color.as_stylesheet()) self.fee_e.setStyleSheet(fee_color.as_stylesheet())
self.feerate_e.setStyleSheet(feerate_color.as_stylesheet())
self.amount_e.textChanged.connect(entry_changed) self.amount_e.textChanged.connect(entry_changed)
self.fee_e.textChanged.connect(entry_changed) self.fee_e.textChanged.connect(entry_changed)
self.feerate_e.textChanged.connect(entry_changed)
self.invoices_label = QLabel(_('Invoices')) self.invoices_label = QLabel(_('Invoices'))
from .invoice_list import InvoiceList from .invoice_list import InvoiceList
@ -1186,8 +1226,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
if not self.config.get('offline') and self.config.is_dynfee() and not self.config.has_fee_estimates(): if not self.config.get('offline') and self.config.is_dynfee() and not self.config.has_fee_estimates():
self.statusBar().showMessage(_('Waiting for fee estimates...')) self.statusBar().showMessage(_('Waiting for fee estimates...'))
return False return False
freeze_fee = (self.fee_e.isModified() freeze_fee = self.is_send_fee_frozen()
and (self.fee_e.text() or self.fee_e.hasFocus())) freeze_feerate = self.is_send_feerate_frozen()
amount = '!' if self.is_max else self.amount_e.get_amount() amount = '!' if self.is_max else self.amount_e.get_amount()
if amount is None: if amount is None:
if not freeze_fee: if not freeze_fee:
@ -1195,7 +1235,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.not_enough_funds = False self.not_enough_funds = False
self.statusBar().showMessage('') self.statusBar().showMessage('')
else: else:
fee = self.fee_e.get_amount() if freeze_fee else None fee_estimator = self.get_send_fee_estimator()
outputs = self.payto_e.get_outputs(self.is_max) outputs = self.payto_e.get_outputs(self.is_max)
if not outputs: if not outputs:
_type, addr = self.get_payto_or_dummy() _type, addr = self.get_payto_or_dummy()
@ -1203,7 +1243,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
try: try:
is_sweep = bool(self.tx_external_keypairs) is_sweep = bool(self.tx_external_keypairs)
tx = self.wallet.make_unsigned_transaction( tx = self.wallet.make_unsigned_transaction(
self.get_coins(), outputs, self.config, fee, is_sweep=is_sweep) self.get_coins(), outputs, self.config,
fixed_fee=fee_estimator, is_sweep=is_sweep)
self.not_enough_funds = False self.not_enough_funds = False
except NotEnoughFunds: except NotEnoughFunds:
self.not_enough_funds = True self.not_enough_funds = True
@ -1211,18 +1252,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.fee_e.setAmount(None) self.fee_e.setAmount(None)
return return
except BaseException: except BaseException:
traceback.print_exc(file=sys.stderr)
return return
size = tx.estimated_size() size = tx.estimated_size()
self.size_e.setAmount(size) self.size_e.setAmount(size)
fee = tx.get_fee()
if not freeze_fee: if not freeze_fee:
fee_rate = self.config.fee_per_kb() fee = None if self.not_enough_funds else fee
fee = None if self.not_enough_funds else tx.get_fee()
self.fee_e.setAmount(fee) self.fee_e.setAmount(fee)
elif fee: if not freeze_feerate:
print(size, fee) fee_rate = fee // size if fee is not None else None
fee_rate = fee // size
self.feerate_e.setAmount(fee_rate) self.feerate_e.setAmount(fee_rate)
if self.is_max: if self.is_max:
@ -1307,6 +1348,26 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
return request_password return request_password
def is_send_fee_frozen(self):
return self.fee_e.isVisible() and self.fee_e.isModified() \
and (self.fee_e.text() or self.fee_e.hasFocus())
def is_send_feerate_frozen(self):
return self.feerate_e.isVisible() and self.feerate_e.isModified() \
and (self.feerate_e.text() or self.feerate_e.hasFocus())
def get_send_fee_estimator(self):
if self.is_send_fee_frozen():
fee_estimator = self.fee_e.get_amount()
elif self.is_send_feerate_frozen():
amount = self.feerate_e.get_amount()
amount = 0 if amount is None else float(amount)
fee_estimator = partial(
simple_config.SimpleConfig.estimate_fee_for_feerate, amount)
else:
fee_estimator = None
return fee_estimator
def read_send_tab(self): def read_send_tab(self):
if self.payment_request and self.payment_request.has_expired(): if self.payment_request and self.payment_request.has_expired():
self.show_error(_('Payment request has expired')) self.show_error(_('Payment request has expired'))
@ -1344,10 +1405,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.show_error(_('Invalid Amount')) self.show_error(_('Invalid Amount'))
return return
freeze_fee = self.fee_e.isVisible() and self.fee_e.isModified() and (self.fee_e.text() or self.fee_e.hasFocus()) fee_estimator = self.get_send_fee_estimator()
fee = self.fee_e.get_amount() if freeze_fee else None
coins = self.get_coins() coins = self.get_coins()
return outputs, fee, label, coins return outputs, fee_estimator, label, coins
def do_preview(self): def do_preview(self):
self.do_send(preview = True) self.do_send(preview = True)
@ -1358,11 +1418,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
r = self.read_send_tab() r = self.read_send_tab()
if not r: if not r:
return return
outputs, fee, tx_desc, coins = r outputs, fee_estimator, tx_desc, coins = r
try: try:
is_sweep = bool(self.tx_external_keypairs) is_sweep = bool(self.tx_external_keypairs)
tx = self.wallet.make_unsigned_transaction( tx = self.wallet.make_unsigned_transaction(
coins, outputs, self.config, fee, is_sweep=is_sweep) coins, outputs, self.config, fixed_fee=fee_estimator,
is_sweep=is_sweep)
except NotEnoughFunds: except NotEnoughFunds:
self.show_message(_("Insufficient funds")) self.show_message(_("Insufficient funds"))
return return
@ -1581,7 +1642,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.not_enough_funds = False self.not_enough_funds = False
self.payment_request = None self.payment_request = None
self.payto_e.is_pr = False self.payto_e.is_pr = False
for e in [self.payto_e, self.message_e, self.amount_e, self.fiat_send_e, self.fee_e]: for e in [self.payto_e, self.message_e, self.amount_e, self.fiat_send_e,
self.fee_e, self.feerate_e, self.size_e]:
e.setText('') e.setText('')
e.setFrozen(False) e.setFrozen(False)
self.set_pay_from([]) self.set_pay_from([])
@ -2522,7 +2584,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
feebox_cb.setToolTip(_("Show fee edit box in send tab.")) feebox_cb.setToolTip(_("Show fee edit box in send tab."))
def on_feebox(x): def on_feebox(x):
self.config.set_key('show_fee', x == Qt.Checked) self.config.set_key('show_fee', x == Qt.Checked)
self.fee_e.setVisible(bool(x)) self.fee_adv_controls.setVisible(bool(x))
feebox_cb.stateChanged.connect(on_feebox) feebox_cb.stateChanged.connect(on_feebox)
fee_widgets.append((feebox_cb, None)) fee_widgets.append((feebox_cb, None))

6
lib/simple_config.py

@ -253,7 +253,11 @@ class SimpleConfig(PrintError):
return fee_rate return fee_rate
def estimate_fee(self, size): def estimate_fee(self, size):
return int(self.fee_per_kb() * size / 1000.) return self.estimate_fee_for_feerate(self.fee_per_kb(), size)
@classmethod
def estimate_fee_for_feerate(cls, fee_per_kb, size):
return int(fee_per_kb * size / 1000.)
def update_fee_estimates(self, key, value): def update_fee_estimates(self, key, value):
self.fee_estimates[key] = value self.fee_estimates[key] = value

10
lib/wallet.py

@ -34,8 +34,12 @@ import time
import json import json
import copy import copy
import errno import errno
import traceback
from functools import partial from functools import partial
from collections import defaultdict from collections import defaultdict
from numbers import Number
import sys
from .i18n import _ from .i18n import _
from .util import NotEnoughFunds, PrintError, UserCancelled, profiler, format_satoshis from .util import NotEnoughFunds, PrintError, UserCancelled, profiler, format_satoshis
@ -903,8 +907,12 @@ class Abstract_Wallet(PrintError):
# Fee estimator # Fee estimator
if fixed_fee is None: if fixed_fee is None:
fee_estimator = config.estimate_fee fee_estimator = config.estimate_fee
else: elif isinstance(fixed_fee, Number):
fee_estimator = lambda size: fixed_fee fee_estimator = lambda size: fixed_fee
elif callable(fixed_fee):
fee_estimator = fixed_fee
else:
raise BaseException('Invalid argument fixed_fee: %s' % fixed_fee)
if i_max is None: if i_max is None:
# Let the coin chooser select the coins to spend # Let the coin chooser select the coins to spend

Loading…
Cancel
Save