From aa6de86c278b3507e87eed63bec4b149b558da78 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Fri, 3 Jul 2015 20:14:12 +0900 Subject: [PATCH] Once a user inputs a fee, retain it. It has long been a minor annoyance that this isn't the default behaviour. Semantics are as follows: - Fees are calculated automatically, as usual, and shown in black - If the user inputs a fee, it is retained, and shown in blue to indicate it is user-overridden - The user can switch back to automatic fee calculation mode in two ways: press Clear, or blank out the fee field - User fees are also cleared by do_clear(), for instance when the payment is sent I have checked all usage combinations I could think of, including the ! case, and all work in a sensible and natural fashion. This also fixes issue #995. --- gui/qt/amountedit.py | 3 +++ gui/qt/main_window.py | 52 ++++++++++++++++++++++++---------------- plugins/exchange_rate.py | 4 ++-- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/gui/qt/amountedit.py b/gui/qt/amountedit.py index 6772a5a9c..653e9e0e4 100644 --- a/gui/qt/amountedit.py +++ b/gui/qt/amountedit.py @@ -45,6 +45,9 @@ class AmountEdit(MyLineEdit): s = s.replace('.','') s = s[:p] + '.' + s[p:p+self.decimal_point()] self.setText(s) + # setText sets Modified to False. Instead we want to remember + # if updates were because of user modification. + self.setModified(self.hasFocus()) self.setCursorPosition(pos) def paintEvent(self, event): diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index e9cb9dd2f..684be7447 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -914,31 +914,37 @@ class ElectrumWindow(QMainWindow): addr = self.payto_e.payto_address if self.payto_e.payto_address else self.dummy_address output = ('address', addr, sendable) dummy_tx = Transaction.from_io(inputs, [output]) - fee = self.wallet.estimated_fee(dummy_tx) - self.amount_e.setAmount(max(0,sendable-fee)) + if not self.fee_e.isModified(): + self.fee_e.setAmount(self.wallet.estimated_fee(dummy_tx)) + self.amount_e.setAmount(max(0, sendable - self.fee_e.get_amount())) self.amount_e.textEdited.emit("") - self.fee_e.setAmount(fee) self.amount_e.shortcut.connect(on_shortcut) - self.payto_e.textChanged.connect(lambda: self.update_fee(False)) - self.amount_e.textEdited.connect(lambda: self.update_fee(False)) - self.fee_e.textEdited.connect(lambda: self.update_fee(True)) + self.payto_e.textChanged.connect(lambda: self.update_fee()) + self.amount_e.textEdited.connect(lambda: self.update_fee()) + self.fee_e.textEdited.connect(lambda: 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(lambda: self.update_fee()) def entry_changed(): - if not self.not_enough_funds: - palette = QPalette() - palette.setColor(self.amount_e.foregroundRole(), QColor('black')) - text = "" - else: - palette = QPalette() - palette.setColor(self.amount_e.foregroundRole(), QColor('red')) + text = "" + if self.not_enough_funds: + amt_color, fee_color = 'red', '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") + ')' + elif self.fee_e.isModified(): + amt_color, fee_color = 'black', 'blue' + else: + amt_color, fee_color = 'black', 'black' self.statusBar().showMessage(text) + palette = QPalette() + palette.setColor(self.amount_e.foregroundRole(), QColor(amt_color)) self.amount_e.setPalette(palette) + palette.setColor(self.amount_e.foregroundRole(), QColor(fee_color)) self.fee_e.setPalette(palette) self.amount_e.textChanged.connect(entry_changed) @@ -970,14 +976,20 @@ class ElectrumWindow(QMainWindow): run_hook('create_send_tab', grid) return w - def update_fee(self, is_fee): + def update_fee(self): + '''Recalculate the fee. If the fee was manually input, retain it, but + still build the TX to see if there are enough funds. + ''' + freeze_fee = (self.fee_e.isModified() + and (self.fee_e.text() or self.fee_e.hasFocus())) outputs = self.payto_e.get_outputs() amount = self.amount_e.get_amount() - fee = self.fee_e.get_amount() if is_fee else None if amount is None: - self.fee_e.setAmount(None) + if not freeze_fee: + self.fee_e.setAmount(None) self.not_enough_funds = False else: + fee = self.fee_e.get_amount() if not outputs: addr = self.payto_e.payto_address if self.payto_e.payto_address else self.dummy_address outputs = [('address', addr, amount)] @@ -986,7 +998,7 @@ class ElectrumWindow(QMainWindow): self.not_enough_funds = False except NotEnoughFunds: self.not_enough_funds = True - if not is_fee: + if not freeze_fee: fee = None if self.not_enough_funds else self.wallet.get_tx_fee(tx) self.fee_e.setAmount(fee) @@ -1287,7 +1299,7 @@ class ElectrumWindow(QMainWindow): def set_frozen_state(self, addrs, freeze): self.wallet.set_frozen_state(addrs, freeze) self.update_address_tab() - self.update_fee(False) + self.update_fee() def create_list_tab(self, l): w = QWidget() @@ -1432,7 +1444,7 @@ class ElectrumWindow(QMainWindow): def send_from_addresses(self, addrs): self.set_pay_from(addrs) self.tabs.setCurrentIndex(1) - self.update_fee(False) + self.update_fee() def paytomany(self): self.tabs.setCurrentIndex(1) @@ -2477,7 +2489,7 @@ class ElectrumWindow(QMainWindow): def on_fee(is_done): self.wallet.set_fee(fee_e.get_amount() or 0, is_done) if not is_done: - self.update_fee(False) + self.update_fee() fee_e.editingFinished.connect(lambda: on_fee(True)) fee_e.textEdited.connect(lambda: on_fee(False)) diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py index 508ac9f13..be7a52c81 100644 --- a/plugins/exchange_rate.py +++ b/plugins/exchange_rate.py @@ -56,7 +56,7 @@ class Exchanger(threading.Thread): def get_json(self, site, get_string): resp = requests.request('GET', 'https://' + site + get_string, headers={"User-Agent":"Electrum"}) return resp.json() - + def exchange(self, btc_amount, quote_currency): with self.lock: if self.quote_currencies is None: @@ -522,7 +522,7 @@ class Plugin(BasePlugin): if exchange_rate is not None: btc_amount = fiat_amount/exchange_rate btc_e.setAmount(int(btc_amount*Decimal(COIN))) - if fee_e: self.win.update_fee(False) + if fee_e: self.win.update_fee() fiat_e.textEdited.connect(fiat_changed) def btc_changed(): if self.exchanger is None: