amountedit.py (4489B)
1 # -*- coding: utf-8 -*- 2 3 from decimal import Decimal 4 from typing import Union 5 6 from PyQt5.QtCore import pyqtSignal, Qt 7 from PyQt5.QtGui import QPalette, QPainter 8 from PyQt5.QtWidgets import (QLineEdit, QStyle, QStyleOptionFrame) 9 10 from .util import char_width_in_lineedit, ColorScheme 11 12 from electrum.util import (format_satoshis_plain, decimal_point_to_base_unit_name, 13 FEERATE_PRECISION, quantize_feerate) 14 15 16 class FreezableLineEdit(QLineEdit): 17 frozen = pyqtSignal() 18 19 def setFrozen(self, b): 20 self.setReadOnly(b) 21 self.setFrame(not b) 22 self.frozen.emit() 23 24 class AmountEdit(FreezableLineEdit): 25 shortcut = pyqtSignal() 26 27 def __init__(self, base_unit, is_int=False, parent=None): 28 QLineEdit.__init__(self, parent) 29 # This seems sufficient for hundred-BTC amounts with 8 decimals 30 self.setFixedWidth(16 * char_width_in_lineedit()) 31 self.base_unit = base_unit 32 self.textChanged.connect(self.numbify) 33 self.is_int = is_int 34 self.is_shortcut = False 35 self.extra_precision = 0 36 37 def decimal_point(self): 38 return 8 39 40 def max_precision(self): 41 return self.decimal_point() + self.extra_precision 42 43 def numbify(self): 44 text = self.text().strip() 45 if text == '!': 46 self.shortcut.emit() 47 return 48 pos = self.cursorPosition() 49 chars = '0123456789' 50 if not self.is_int: chars +='.' 51 s = ''.join([i for i in text if i in chars]) 52 if not self.is_int: 53 if '.' in s: 54 p = s.find('.') 55 s = s.replace('.','') 56 s = s[:p] + '.' + s[p:p+self.max_precision()] 57 self.setText(s) 58 # setText sets Modified to False. Instead we want to remember 59 # if updates were because of user modification. 60 self.setModified(self.hasFocus()) 61 self.setCursorPosition(pos) 62 63 def paintEvent(self, event): 64 QLineEdit.paintEvent(self, event) 65 if self.base_unit: 66 panel = QStyleOptionFrame() 67 self.initStyleOption(panel) 68 textRect = self.style().subElementRect(QStyle.SE_LineEditContents, panel, self) 69 textRect.adjust(2, 0, -10, 0) 70 painter = QPainter(self) 71 painter.setPen(ColorScheme.GRAY.as_color()) 72 painter.drawText(textRect, int(Qt.AlignRight | Qt.AlignVCenter), self.base_unit()) 73 74 def get_amount(self) -> Union[None, Decimal, int]: 75 try: 76 return (int if self.is_int else Decimal)(str(self.text())) 77 except: 78 return None 79 80 def setAmount(self, x): 81 self.setText("%d"%x) 82 83 84 class BTCAmountEdit(AmountEdit): 85 86 def __init__(self, decimal_point, is_int=False, parent=None): 87 AmountEdit.__init__(self, self._base_unit, is_int, parent) 88 self.decimal_point = decimal_point 89 90 def _base_unit(self): 91 return decimal_point_to_base_unit_name(self.decimal_point()) 92 93 def get_amount(self): 94 # returns amt in satoshis 95 try: 96 x = Decimal(str(self.text())) 97 except: 98 return None 99 # scale it to max allowed precision, make it an int 100 power = pow(10, self.max_precision()) 101 max_prec_amount = int(power * x) 102 # if the max precision is simply what unit conversion allows, just return 103 if self.max_precision() == self.decimal_point(): 104 return max_prec_amount 105 # otherwise, scale it back to the expected unit 106 amount = Decimal(max_prec_amount) / pow(10, self.max_precision()-self.decimal_point()) 107 return Decimal(amount) if not self.is_int else int(amount) 108 109 def setAmount(self, amount_sat): 110 if amount_sat is None: 111 self.setText(" ") # Space forces repaint in case units changed 112 else: 113 self.setText(format_satoshis_plain(amount_sat, decimal_point=self.decimal_point())) 114 self.repaint() # macOS hack for #6269 115 116 117 class FeerateEdit(BTCAmountEdit): 118 119 def __init__(self, decimal_point, is_int=False, parent=None): 120 super().__init__(decimal_point, is_int, parent) 121 self.extra_precision = FEERATE_PRECISION 122 123 def _base_unit(self): 124 return 'sat/byte' 125 126 def get_amount(self): 127 sat_per_byte_amount = BTCAmountEdit.get_amount(self) 128 return quantize_feerate(sat_per_byte_amount) 129 130 def setAmount(self, amount): 131 amount = quantize_feerate(amount) 132 super().setAmount(amount)