tlocktimeedit.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tlocktimeedit.py (6133B)
---
1 # Copyright (C) 2020 The Electrum developers
2 # Distributed under the MIT software license, see the accompanying
3 # file LICENCE or http://www.opensource.org/licenses/mit-license.php
4
5 import time
6 from datetime import datetime
7 from typing import Optional, Any
8
9 from PyQt5.QtCore import Qt, QDateTime
10 from PyQt5.QtGui import QPalette, QPainter
11 from PyQt5.QtWidgets import (QWidget, QLineEdit, QStyle, QStyleOptionFrame, QComboBox,
12 QHBoxLayout, QDateTimeEdit)
13
14 from electrum.i18n import _
15 from electrum.bitcoin import NLOCKTIME_MIN, NLOCKTIME_MAX, NLOCKTIME_BLOCKHEIGHT_MAX
16
17 from .util import char_width_in_lineedit, ColorScheme
18
19
20 class LockTimeEdit(QWidget):
21
22 def __init__(self, parent=None):
23 QWidget.__init__(self, parent)
24
25 hbox = QHBoxLayout()
26 self.setLayout(hbox)
27 hbox.setContentsMargins(0, 0, 0, 0)
28 hbox.setSpacing(0)
29
30 self.locktime_raw_e = LockTimeRawEdit(self)
31 self.locktime_height_e = LockTimeHeightEdit(self)
32 self.locktime_date_e = LockTimeDateEdit(self)
33 self.editors = [self.locktime_raw_e, self.locktime_height_e, self.locktime_date_e]
34
35 self.combo = QComboBox()
36 options = [_("Raw"), _("Block height"), _("Date")]
37 option_index_to_editor_map = {
38 0: self.locktime_raw_e,
39 1: self.locktime_height_e,
40 2: self.locktime_date_e,
41 }
42 default_index = 1
43 self.combo.addItems(options)
44
45 def on_current_index_changed(i):
46 for w in self.editors:
47 w.setVisible(False)
48 w.setEnabled(False)
49 prev_locktime = self.editor.get_locktime()
50 self.editor = option_index_to_editor_map[i]
51 if self.editor.is_acceptable_locktime(prev_locktime):
52 self.editor.set_locktime(prev_locktime)
53 self.editor.setVisible(True)
54 self.editor.setEnabled(True)
55
56 self.editor = option_index_to_editor_map[default_index]
57 self.combo.currentIndexChanged.connect(on_current_index_changed)
58 self.combo.setCurrentIndex(default_index)
59 on_current_index_changed(default_index)
60
61 hbox.addWidget(self.combo)
62 for w in self.editors:
63 hbox.addWidget(w)
64 hbox.addStretch(1)
65
66 def get_locktime(self) -> Optional[int]:
67 return self.editor.get_locktime()
68
69 def set_locktime(self, x: Any) -> None:
70 self.editor.set_locktime(x)
71
72
73 class _LockTimeEditor:
74 min_allowed_value = NLOCKTIME_MIN
75 max_allowed_value = NLOCKTIME_MAX
76
77 def get_locktime(self) -> Optional[int]:
78 raise NotImplementedError()
79
80 def set_locktime(self, x: Any) -> None:
81 raise NotImplementedError()
82
83 @classmethod
84 def is_acceptable_locktime(cls, x: Any) -> bool:
85 if not x: # e.g. empty string
86 return True
87 try:
88 x = int(x)
89 except:
90 return False
91 return cls.min_allowed_value <= x <= cls.max_allowed_value
92
93
94 class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
95
96 def __init__(self, parent=None):
97 QLineEdit.__init__(self, parent)
98 self.setFixedWidth(14 * char_width_in_lineedit())
99 self.textChanged.connect(self.numbify)
100
101 def numbify(self):
102 text = self.text().strip()
103 chars = '0123456789'
104 pos = self.cursorPosition()
105 pos = len(''.join([i for i in text[:pos] if i in chars]))
106 s = ''.join([i for i in text if i in chars])
107 self.set_locktime(s)
108 # setText sets Modified to False. Instead we want to remember
109 # if updates were because of user modification.
110 self.setModified(self.hasFocus())
111 self.setCursorPosition(pos)
112
113 def get_locktime(self) -> Optional[int]:
114 try:
115 return int(str(self.text()))
116 except:
117 return None
118
119 def set_locktime(self, x: Any) -> None:
120 try:
121 x = int(x)
122 except:
123 self.setText('')
124 return
125 x = max(x, self.min_allowed_value)
126 x = min(x, self.max_allowed_value)
127 self.setText(str(x))
128
129
130 class LockTimeHeightEdit(LockTimeRawEdit):
131 max_allowed_value = NLOCKTIME_BLOCKHEIGHT_MAX
132
133 def __init__(self, parent=None):
134 LockTimeRawEdit.__init__(self, parent)
135 self.setFixedWidth(20 * char_width_in_lineedit())
136
137 def paintEvent(self, event):
138 super().paintEvent(event)
139 panel = QStyleOptionFrame()
140 self.initStyleOption(panel)
141 textRect = self.style().subElementRect(QStyle.SE_LineEditContents, panel, self)
142 textRect.adjust(2, 0, -10, 0)
143 painter = QPainter(self)
144 painter.setPen(ColorScheme.GRAY.as_color())
145 painter.drawText(textRect, Qt.AlignRight | Qt.AlignVCenter, "height")
146
147
148 def get_max_allowed_timestamp() -> int:
149 ts = NLOCKTIME_MAX
150 # Test if this value is within the valid timestamp limits (which is platform-dependent).
151 # see #6170
152 try:
153 datetime.fromtimestamp(ts)
154 except (OSError, OverflowError):
155 ts = 2 ** 31 - 1 # INT32_MAX
156 datetime.fromtimestamp(ts) # test if raises
157 return ts
158
159
160 class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
161 min_allowed_value = NLOCKTIME_BLOCKHEIGHT_MAX + 1
162 max_allowed_value = get_max_allowed_timestamp()
163
164 def __init__(self, parent=None):
165 QDateTimeEdit.__init__(self, parent)
166 self.setMinimumDateTime(datetime.fromtimestamp(self.min_allowed_value))
167 self.setMaximumDateTime(datetime.fromtimestamp(self.max_allowed_value))
168 self.setDateTime(QDateTime.currentDateTime())
169
170 def get_locktime(self) -> Optional[int]:
171 dt = self.dateTime().toPyDateTime()
172 locktime = int(time.mktime(dt.timetuple()))
173 return locktime
174
175 def set_locktime(self, x: Any) -> None:
176 if not self.is_acceptable_locktime(x):
177 self.setDateTime(QDateTime.currentDateTime())
178 return
179 try:
180 x = int(x)
181 except:
182 self.setDateTime(QDateTime.currentDateTime())
183 return
184 dt = datetime.fromtimestamp(x)
185 self.setDateTime(dt)