Browse Source

Merge pull request #4033 from Lastrellik/TextCompleter

Text completer
3.2.x
ghost43 7 years ago
committed by GitHub
parent
commit
9a0cf63769
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 120
      gui/qt/completion_text_edit.py
  2. 2
      gui/qt/main_window.py
  3. 74
      gui/qt/paytoedit.py
  4. 24
      gui/qt/seed_dialog.py

120
gui/qt/completion_text_edit.py

@ -0,0 +1,120 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2018 The Electrum developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from .util import ButtonsTextEdit
class CompletionTextEdit(ButtonsTextEdit):
def __init__(self, parent=None):
super(CompletionTextEdit, self).__init__(parent)
self.completer = None
self.moveCursor(QTextCursor.End)
self.disable_suggestions()
def set_completer(self, completer):
self.completer = completer
self.initialize_completer()
def initialize_completer(self):
self.completer.setWidget(self)
self.completer.setCompletionMode(QCompleter.PopupCompletion)
self.completer.activated.connect(self.insert_completion)
self.enable_suggestions()
def insert_completion(self, completion):
if self.completer.widget() != self:
return
text_cursor = self.textCursor()
extra = len(completion) - len(self.completer.completionPrefix())
text_cursor.movePosition(QTextCursor.Left)
text_cursor.movePosition(QTextCursor.EndOfWord)
if extra == 0:
text_cursor.insertText(" ")
else:
text_cursor.insertText(completion[-extra:] + " ")
self.setTextCursor(text_cursor)
def text_under_cursor(self):
tc = self.textCursor()
tc.select(QTextCursor.WordUnderCursor)
return tc.selectedText()
def enable_suggestions(self):
self.suggestions_enabled = True
def disable_suggestions(self):
self.suggestions_enabled = False
def keyPressEvent(self, e):
if self.isReadOnly():
return
if self.is_special_key(e):
e.ignore()
return
QPlainTextEdit.keyPressEvent(self, e)
ctrlOrShift = e.modifiers() and (Qt.ControlModifier or Qt.ShiftModifier)
if self.completer is None or (ctrlOrShift and not e.text()):
return
if not self.suggestions_enabled:
return
eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="
hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift
completionPrefix = self.text_under_cursor()
if hasModifier or not e.text() or len(completionPrefix) < 1 or eow.find(e.text()[-1]) >= 0:
self.completer.popup().hide()
return
if completionPrefix != self.completer.completionPrefix():
self.completer.setCompletionPrefix(completionPrefix)
self.completer.popup().setCurrentIndex(self.completer.completionModel().index(0, 0))
cr = self.cursorRect()
cr.setWidth(self.completer.popup().sizeHintForColumn(0) + self.completer.popup().verticalScrollBar().sizeHint().width())
self.completer.complete(cr)
def is_special_key(self, e):
if self.completer != None and self.completer.popup().isVisible():
if e.key() in [Qt.Key_Enter, Qt.Key_Return]:
return True
if e.key() in [Qt.Key_Tab, Qt.Key_Down, Qt.Key_Up]:
return True
return False
if __name__ == "__main__":
app = QApplication([])
completer = QCompleter(["alabama", "arkansas", "avocado", "breakfast", "sausage"])
te = CompletionTextEdit()
te.set_completer(completer)
te.show()
app.exec_()

2
gui/qt/main_window.py

@ -1050,7 +1050,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
completer = QCompleter() completer = QCompleter()
completer.setCaseSensitivity(False) completer.setCaseSensitivity(False)
self.payto_e.setCompleter(completer) self.payto_e.set_completer(completer)
completer.setModel(self.completions) completer.setModel(self.completions)
msg = _('Description of the transaction (not mandatory).') + '\n\n'\ msg = _('Description of the transaction (not mandatory).') + '\n\n'\

74
gui/qt/paytoedit.py

@ -23,16 +23,15 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
from PyQt5.QtCore import *
from PyQt5.QtGui import * from PyQt5.QtGui import *
from PyQt5.QtWidgets import QCompleter, QPlainTextEdit
from .qrtextedit import ScanQRTextEdit
import re import re
from decimal import Decimal from decimal import Decimal
from electrum import bitcoin from electrum import bitcoin
from electrum.util import bfh from electrum.util import bfh
from .qrtextedit import ScanQRTextEdit
from .completion_text_edit import CompletionTextEdit
from . import util from . import util
RE_ADDRESS = '[1-9A-HJ-NP-Za-km-z]{26,}' RE_ADDRESS = '[1-9A-HJ-NP-Za-km-z]{26,}'
@ -41,9 +40,10 @@ RE_ALIAS = '(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>'
frozen_style = "QWidget { background-color:none; border:none;}" frozen_style = "QWidget { background-color:none; border:none;}"
normal_style = "QPlainTextEdit { }" normal_style = "QPlainTextEdit { }"
class PayToEdit(ScanQRTextEdit): class PayToEdit(CompletionTextEdit, ScanQRTextEdit):
def __init__(self, win): def __init__(self, win):
CompletionTextEdit.__init__(self)
ScanQRTextEdit.__init__(self) ScanQRTextEdit.__init__(self)
self.win = win self.win = win
self.amount_edit = win.amount_e self.amount_edit = win.amount_e
@ -198,70 +198,6 @@ class PayToEdit(ScanQRTextEdit):
self.setMaximumHeight(h) self.setMaximumHeight(h)
self.verticalScrollBar().hide() self.verticalScrollBar().hide()
def setCompleter(self, completer):
self.c = completer
self.c.setWidget(self)
self.c.setCompletionMode(QCompleter.PopupCompletion)
self.c.activated.connect(self.insertCompletion)
def insertCompletion(self, completion):
if self.c.widget() != self:
return
tc = self.textCursor()
extra = len(completion) - len(self.c.completionPrefix())
tc.movePosition(QTextCursor.Left)
tc.movePosition(QTextCursor.EndOfWord)
tc.insertText(completion[-extra:])
self.setTextCursor(tc)
def textUnderCursor(self):
tc = self.textCursor()
tc.select(QTextCursor.WordUnderCursor)
return tc.selectedText()
def keyPressEvent(self, e):
if self.isReadOnly():
return
if self.c.popup().isVisible():
if e.key() in [Qt.Key_Enter, Qt.Key_Return]:
e.ignore()
return
if e.key() in [Qt.Key_Tab]:
e.ignore()
return
if e.key() in [Qt.Key_Down, Qt.Key_Up] and not self.is_multiline():
e.ignore()
return
QPlainTextEdit.keyPressEvent(self, e)
ctrlOrShift = e.modifiers() and (Qt.ControlModifier or Qt.ShiftModifier)
if self.c is None or (ctrlOrShift and not e.text()):
return
eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="
hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift
completionPrefix = self.textUnderCursor()
if hasModifier or not e.text() or len(completionPrefix) < 1 or eow.find(e.text()[-1]) >= 0:
self.c.popup().hide()
return
if completionPrefix != self.c.completionPrefix():
self.c.setCompletionPrefix(completionPrefix)
self.c.popup().setCurrentIndex(self.c.completionModel().index(0, 0))
cr = self.cursorRect()
cr.setWidth(self.c.popup().sizeHintForColumn(0) + self.c.popup().verticalScrollBar().sizeHint().width())
self.c.complete(cr)
def qr_input(self): def qr_input(self):
data = super(PayToEdit,self).qr_input() data = super(PayToEdit,self).qr_input()
if data.startswith("bitcoin:"): if data.startswith("bitcoin:"):

24
gui/qt/seed_dialog.py

@ -23,13 +23,13 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from electrum.i18n import _ from electrum.i18n import _
from electrum.mnemonic import Mnemonic
import electrum.old_mnemonic
from .util import * from .util import *
from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit
from .completion_text_edit import CompletionTextEdit
def seed_warning_msg(seed): def seed_warning_msg(seed):
@ -92,7 +92,7 @@ class SeedLayout(QVBoxLayout):
self.options = options self.options = options
if title: if title:
self.addWidget(WWLabel(title)) self.addWidget(WWLabel(title))
self.seed_e = ButtonsTextEdit() self.seed_e = CompletionTextEdit()
if seed: if seed:
self.seed_e.setText(seed) self.seed_e.setText(seed)
else: else:
@ -100,6 +100,8 @@ class SeedLayout(QVBoxLayout):
self.is_seed = is_seed self.is_seed = is_seed
self.saved_is_seed = self.is_seed self.saved_is_seed = self.is_seed
self.seed_e.textChanged.connect(self.on_edit) self.seed_e.textChanged.connect(self.on_edit)
self.initialize_completer()
self.seed_e.setMaximumHeight(75) self.seed_e.setMaximumHeight(75)
hbox = QHBoxLayout() hbox = QHBoxLayout()
if icon: if icon:
@ -131,6 +133,14 @@ class SeedLayout(QVBoxLayout):
self.seed_warning.setText(seed_warning_msg(seed)) self.seed_warning.setText(seed_warning_msg(seed))
self.addWidget(self.seed_warning) self.addWidget(self.seed_warning)
def initialize_completer(self):
english_list = Mnemonic('en').wordlist
old_list = electrum.old_mnemonic.words
self.wordlist = english_list + list(set(old_list) - set(english_list)) #concat both lists
self.wordlist.sort()
self.completer = QCompleter(self.wordlist)
self.seed_e.set_completer(self.completer)
def get_seed(self): def get_seed(self):
text = self.seed_e.text() text = self.seed_e.text()
return ' '.join(text.split()) return ' '.join(text.split())
@ -150,6 +160,12 @@ class SeedLayout(QVBoxLayout):
self.seed_type_label.setText(label) self.seed_type_label.setText(label)
self.parent.next_button.setEnabled(b) self.parent.next_button.setEnabled(b)
# to account for bip39 seeds
for word in self.get_seed().split(" ")[:-1]:
if word not in self.wordlist:
self.seed_e.disable_suggestions()
return
self.seed_e.enable_suggestions()
class KeysLayout(QVBoxLayout): class KeysLayout(QVBoxLayout):
def __init__(self, parent=None, title=None, is_valid=None, allow_multi=False): def __init__(self, parent=None, title=None, is_valid=None, allow_multi=False):

Loading…
Cancel
Save