URI: 
       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)