connect fee edits together
This commit is contained in:
parent
96c86dac27
commit
3ba864bbca
@ -81,7 +81,6 @@ class BTCAmountEdit(AmountEdit):
|
||||
|
||||
def _base_unit(self):
|
||||
p = self.decimal_point()
|
||||
assert p in [2, 5, 8]
|
||||
if p == 8:
|
||||
return 'BTC'
|
||||
if p == 5:
|
||||
@ -104,6 +103,18 @@ class BTCAmountEdit(AmountEdit):
|
||||
else:
|
||||
self.setText(format_satoshis_plain(amount, self.decimal_point()))
|
||||
|
||||
class BTCkBEdit(BTCAmountEdit):
|
||||
|
||||
class FeerateEdit(BTCAmountEdit):
|
||||
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
|
||||
|
||||
@ -38,7 +38,7 @@ from PyQt5.QtWidgets import *
|
||||
|
||||
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.plugins import run_hook
|
||||
from electrum.i18n import _
|
||||
@ -54,7 +54,7 @@ try:
|
||||
except:
|
||||
plot_history = None
|
||||
|
||||
from .amountedit import AmountEdit, BTCAmountEdit, MyLineEdit
|
||||
from .amountedit import AmountEdit, BTCAmountEdit, MyLineEdit, FeerateEdit
|
||||
from .qrcodewidget import QRCodeWidget, QRDialog
|
||||
from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit
|
||||
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.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.setReadOnly(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.size_e.setFrozen(True)
|
||||
|
||||
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)
|
||||
|
||||
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.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))
|
||||
|
||||
self.connect_fields(self, self.amount_e, self.fiat_send_e, self.fee_e)
|
||||
|
||||
#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.setVisible(False)
|
||||
|
||||
grid.addWidget(self.fee_e_label, 5, 0)
|
||||
grid.addWidget(self.feerate_e, 5, 1)
|
||||
grid.addWidget(self.size_e, 5, 2)
|
||||
grid.addWidget(self.fee_e, 5, 3)
|
||||
vbox_feelabel = QVBoxLayout()
|
||||
vbox_feelabel.addWidget(self.fee_e_label)
|
||||
vbox_feelabel.addStretch(1)
|
||||
grid.addLayout(vbox_feelabel, 5, 0)
|
||||
|
||||
grid.addWidget(self.fee_slider, 6, 1)
|
||||
#grid.addWidget(self.rbf_checkbox, 5, 3)
|
||||
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)
|
||||
|
||||
self.size_e.setStyleSheet(ColorScheme.BLUE.as_stylesheet())
|
||||
|
||||
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.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.preview_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.payto_e.textChanged.connect(self.update_fee)
|
||||
@ -1126,26 +1152,40 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
|
||||
def entry_changed():
|
||||
text = ""
|
||||
|
||||
amt_color = ColorScheme.DEFAULT
|
||||
fee_color = ColorScheme.DEFAULT
|
||||
feerate_color = ColorScheme.DEFAULT
|
||||
|
||||
if self.not_enough_funds:
|
||||
amt_color, fee_color = ColorScheme.RED, ColorScheme.RED
|
||||
feerate_color = ColorScheme.RED
|
||||
text = _( "Not enough funds" )
|
||||
c, u, x = self.wallet.get_frozen_balance()
|
||||
if c+u+x:
|
||||
text += ' (' + self.format_amount(c+u+x).strip() + ' ' + self.base_unit() + ' ' +_("are frozen") + ')'
|
||||
|
||||
# blue color denotes auto-filled values
|
||||
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():
|
||||
amt_color, fee_color = ColorScheme.DEFAULT, ColorScheme.BLUE
|
||||
fee_color = ColorScheme.BLUE
|
||||
feerate_color = ColorScheme.BLUE
|
||||
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.amount_e.setStyleSheet(amt_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.fee_e.textChanged.connect(entry_changed)
|
||||
self.feerate_e.textChanged.connect(entry_changed)
|
||||
|
||||
self.invoices_label = QLabel(_('Invoices'))
|
||||
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():
|
||||
self.statusBar().showMessage(_('Waiting for fee estimates...'))
|
||||
return False
|
||||
freeze_fee = (self.fee_e.isModified()
|
||||
and (self.fee_e.text() or self.fee_e.hasFocus()))
|
||||
freeze_fee = self.is_send_fee_frozen()
|
||||
freeze_feerate = self.is_send_feerate_frozen()
|
||||
amount = '!' if self.is_max else self.amount_e.get_amount()
|
||||
if amount is None:
|
||||
if not freeze_fee:
|
||||
@ -1195,7 +1235,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
self.not_enough_funds = False
|
||||
self.statusBar().showMessage('')
|
||||
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)
|
||||
if not outputs:
|
||||
_type, addr = self.get_payto_or_dummy()
|
||||
@ -1203,7 +1243,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
try:
|
||||
is_sweep = bool(self.tx_external_keypairs)
|
||||
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
|
||||
except NotEnoughFunds:
|
||||
self.not_enough_funds = True
|
||||
@ -1211,18 +1252,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
self.fee_e.setAmount(None)
|
||||
return
|
||||
except BaseException:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
return
|
||||
|
||||
size = tx.estimated_size()
|
||||
self.size_e.setAmount(size)
|
||||
|
||||
|
||||
fee = tx.get_fee()
|
||||
if not freeze_fee:
|
||||
fee_rate = self.config.fee_per_kb()
|
||||
fee = None if self.not_enough_funds else tx.get_fee()
|
||||
fee = None if self.not_enough_funds else fee
|
||||
self.fee_e.setAmount(fee)
|
||||
elif fee:
|
||||
print(size, fee)
|
||||
fee_rate = fee // size
|
||||
if not freeze_feerate:
|
||||
fee_rate = fee // size if fee is not None else None
|
||||
self.feerate_e.setAmount(fee_rate)
|
||||
|
||||
if self.is_max:
|
||||
@ -1307,6 +1348,26 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
return func(self, *args, **kwargs)
|
||||
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):
|
||||
if self.payment_request and self.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'))
|
||||
return
|
||||
|
||||
freeze_fee = self.fee_e.isVisible() and self.fee_e.isModified() and (self.fee_e.text() or self.fee_e.hasFocus())
|
||||
fee = self.fee_e.get_amount() if freeze_fee else None
|
||||
fee_estimator = self.get_send_fee_estimator()
|
||||
coins = self.get_coins()
|
||||
return outputs, fee, label, coins
|
||||
return outputs, fee_estimator, label, coins
|
||||
|
||||
def do_preview(self):
|
||||
self.do_send(preview = True)
|
||||
@ -1358,11 +1418,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
r = self.read_send_tab()
|
||||
if not r:
|
||||
return
|
||||
outputs, fee, tx_desc, coins = r
|
||||
outputs, fee_estimator, tx_desc, coins = r
|
||||
try:
|
||||
is_sweep = bool(self.tx_external_keypairs)
|
||||
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:
|
||||
self.show_message(_("Insufficient funds"))
|
||||
return
|
||||
@ -1581,7 +1642,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
self.not_enough_funds = False
|
||||
self.payment_request = None
|
||||
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.setFrozen(False)
|
||||
self.set_pay_from([])
|
||||
@ -2522,7 +2584,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
feebox_cb.setToolTip(_("Show fee edit box in send tab."))
|
||||
def on_feebox(x):
|
||||
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)
|
||||
fee_widgets.append((feebox_cb, None))
|
||||
|
||||
|
||||
@ -253,7 +253,11 @@ class SimpleConfig(PrintError):
|
||||
return fee_rate
|
||||
|
||||
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):
|
||||
self.fee_estimates[key] = value
|
||||
|
||||
@ -34,8 +34,12 @@ import time
|
||||
import json
|
||||
import copy
|
||||
import errno
|
||||
import traceback
|
||||
from functools import partial
|
||||
from collections import defaultdict
|
||||
from numbers import Number
|
||||
|
||||
import sys
|
||||
|
||||
from .i18n import _
|
||||
from .util import NotEnoughFunds, PrintError, UserCancelled, profiler, format_satoshis
|
||||
@ -903,8 +907,12 @@ class Abstract_Wallet(PrintError):
|
||||
# Fee estimator
|
||||
if fixed_fee is None:
|
||||
fee_estimator = config.estimate_fee
|
||||
else:
|
||||
elif isinstance(fixed_fee, Number):
|
||||
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:
|
||||
# Let the coin chooser select the coins to spend
|
||||
|
||||
Loading…
Reference in New Issue
Block a user