1#!/usr/local/bin/python3.8 2# 3# Electrum - lightweight Bitcoin client 4# Copyright (C) 2018 The Electrum developers 5# 6# Permission is hereby granted, free of charge, to any person 7# obtaining a copy of this software and associated documentation files 8# (the "Software"), to deal in the Software without restriction, 9# including without limitation the rights to use, copy, modify, merge, 10# publish, distribute, sublicense, and/or sell copies of the Software, 11# and to permit persons to whom the Software is furnished to do so, 12# subject to the following conditions: 13# 14# The above copyright notice and this permission notice shall be 15# included in all copies or substantial portions of the Software. 16# 17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24# SOFTWARE. 25 26from PyQt5.QtGui import QTextCursor 27from PyQt5.QtCore import Qt 28from PyQt5.QtWidgets import QCompleter, QPlainTextEdit, QApplication 29 30from .util import ButtonsTextEdit 31 32 33class CompletionTextEdit(ButtonsTextEdit): 34 35 def __init__(self): 36 ButtonsTextEdit.__init__(self) 37 self.completer = None 38 self.moveCursor(QTextCursor.End) 39 self.disable_suggestions() 40 41 def set_completer(self, completer): 42 self.completer = completer 43 self.initialize_completer() 44 45 def initialize_completer(self): 46 self.completer.setWidget(self) 47 self.completer.setCompletionMode(QCompleter.PopupCompletion) 48 self.completer.activated.connect(self.insert_completion) 49 self.enable_suggestions() 50 51 def insert_completion(self, completion): 52 if self.completer.widget() != self: 53 return 54 text_cursor = self.textCursor() 55 extra = len(completion) - len(self.completer.completionPrefix()) 56 text_cursor.movePosition(QTextCursor.Left) 57 text_cursor.movePosition(QTextCursor.EndOfWord) 58 if extra == 0: 59 text_cursor.insertText(" ") 60 else: 61 text_cursor.insertText(completion[-extra:] + " ") 62 self.setTextCursor(text_cursor) 63 64 def text_under_cursor(self): 65 tc = self.textCursor() 66 tc.select(QTextCursor.WordUnderCursor) 67 return tc.selectedText() 68 69 def enable_suggestions(self): 70 self.suggestions_enabled = True 71 72 def disable_suggestions(self): 73 self.suggestions_enabled = False 74 75 def keyPressEvent(self, e): 76 if self.isReadOnly(): 77 return 78 79 if self.is_special_key(e): 80 e.ignore() 81 return 82 83 QPlainTextEdit.keyPressEvent(self, e) 84 85 ctrlOrShift = e.modifiers() and (Qt.ControlModifier or Qt.ShiftModifier) 86 if self.completer is None or (ctrlOrShift and not e.text()): 87 return 88 89 if not self.suggestions_enabled: 90 return 91 92 eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" 93 hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift 94 completionPrefix = self.text_under_cursor() 95 96 if hasModifier or not e.text() or len(completionPrefix) < 1 or eow.find(e.text()[-1]) >= 0: 97 self.completer.popup().hide() 98 return 99 100 if completionPrefix != self.completer.completionPrefix(): 101 self.completer.setCompletionPrefix(completionPrefix) 102 self.completer.popup().setCurrentIndex(self.completer.completionModel().index(0, 0)) 103 104 cr = self.cursorRect() 105 cr.setWidth(self.completer.popup().sizeHintForColumn(0) + self.completer.popup().verticalScrollBar().sizeHint().width()) 106 self.completer.complete(cr) 107 108 def is_special_key(self, e): 109 if self.completer and self.completer.popup().isVisible(): 110 if e.key() in (Qt.Key_Enter, Qt.Key_Return): 111 return True 112 if e.key() == Qt.Key_Tab: 113 return True 114 return False 115 116if __name__ == "__main__": 117 app = QApplication([]) 118 completer = QCompleter(["alabama", "arkansas", "avocado", "breakfast", "sausage"]) 119 te = CompletionTextEdit() 120 te.set_completer(completer) 121 te.show() 122 app.exec_() 123