1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 Riverbank Computing Limited. 7## Copyright (C) 2012 Digia Plc 8## All rights reserved. 9## 10## This file is part of the examples of PyQt. 11## 12## $QT_BEGIN_LICENSE:BSD$ 13## You may use this file under the terms of the BSD license as follows: 14## 15## "Redistribution and use in source and binary forms, with or without 16## modification, are permitted provided that the following conditions are 17## met: 18## * Redistributions of source code must retain the above copyright 19## notice, this list of conditions and the following disclaimer. 20## * Redistributions in binary form must reproduce the above copyright 21## notice, this list of conditions and the following disclaimer in 22## the documentation and/or other materials provided with the 23## distribution. 24## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 25## the names of its contributors may be used to endorse or promote 26## products derived from this software without specific prior written 27## permission. 28## 29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 40## $QT_END_LICENSE$ 41## 42############################################################################# 43 44 45from PyQt5.QtCore import QFile, QStringListModel, Qt 46from PyQt5.QtGui import QCursor, QKeySequence, QTextCursor 47from PyQt5.QtWidgets import (QAction, QApplication, QCompleter, QMainWindow, 48 QMessageBox, QTextEdit) 49 50import customcompleter_rc 51 52 53class TextEdit(QTextEdit): 54 def __init__(self, parent=None): 55 super(TextEdit, self).__init__(parent) 56 57 self._completer = None 58 59 self.setPlainText( 60 "This TextEdit provides autocompletions for words that have " 61 "more than 3 characters. You can trigger autocompletion " 62 "using %s" % QKeySequence("Ctrl+E").toString( 63 QKeySequence.NativeText)) 64 65 def setCompleter(self, c): 66 if self._completer is not None: 67 self._completer.activated.disconnect() 68 69 self._completer = c 70 71 c.setWidget(self) 72 c.setCompletionMode(QCompleter.PopupCompletion) 73 c.setCaseSensitivity(Qt.CaseInsensitive) 74 c.activated.connect(self.insertCompletion) 75 76 def completer(self): 77 return self._completer 78 79 def insertCompletion(self, completion): 80 if self._completer.widget() is not self: 81 return 82 83 tc = self.textCursor() 84 extra = len(completion) - len(self._completer.completionPrefix()) 85 tc.movePosition(QTextCursor.Left) 86 tc.movePosition(QTextCursor.EndOfWord) 87 tc.insertText(completion[-extra:]) 88 self.setTextCursor(tc) 89 90 def textUnderCursor(self): 91 tc = self.textCursor() 92 tc.select(QTextCursor.WordUnderCursor) 93 94 return tc.selectedText() 95 96 def focusInEvent(self, e): 97 if self._completer is not None: 98 self._completer.setWidget(self) 99 100 super(TextEdit, self).focusInEvent(e) 101 102 def keyPressEvent(self, e): 103 if self._completer is not None and self._completer.popup().isVisible(): 104 # The following keys are forwarded by the completer to the widget. 105 if e.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab): 106 e.ignore() 107 # Let the completer do default behavior. 108 return 109 110 isShortcut = ((e.modifiers() & Qt.ControlModifier) != 0 and e.key() == Qt.Key_E) 111 if self._completer is None or not isShortcut: 112 # Do not process the shortcut when we have a completer. 113 super(TextEdit, self).keyPressEvent(e) 114 115 ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier) 116 if self._completer is None or (ctrlOrShift and len(e.text()) == 0): 117 return 118 119 eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" 120 hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift 121 completionPrefix = self.textUnderCursor() 122 123 if not isShortcut and (hasModifier or len(e.text()) == 0 or len(completionPrefix) < 3 or e.text()[-1] in eow): 124 self._completer.popup().hide() 125 return 126 127 if completionPrefix != self._completer.completionPrefix(): 128 self._completer.setCompletionPrefix(completionPrefix) 129 self._completer.popup().setCurrentIndex( 130 self._completer.completionModel().index(0, 0)) 131 132 cr = self.cursorRect() 133 cr.setWidth(self._completer.popup().sizeHintForColumn(0) + self._completer.popup().verticalScrollBar().sizeHint().width()) 134 self._completer.complete(cr) 135 136 137class MainWindow(QMainWindow): 138 def __init__(self, parent=None): 139 super(MainWindow, self).__init__(parent) 140 141 self.createMenu() 142 143 self.completingTextEdit = TextEdit() 144 self.completer = QCompleter(self) 145 self.completer.setModel(self.modelFromFile(':/resources/wordlist.txt')) 146 self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel) 147 self.completer.setCaseSensitivity(Qt.CaseInsensitive) 148 self.completer.setWrapAround(False) 149 self.completingTextEdit.setCompleter(self.completer) 150 151 self.setCentralWidget(self.completingTextEdit) 152 self.resize(500, 300) 153 self.setWindowTitle("Completer") 154 155 def createMenu(self): 156 exitAction = QAction("Exit", self) 157 aboutAct = QAction("About", self) 158 aboutQtAct = QAction("About Qt", self) 159 160 exitAction.triggered.connect(QApplication.instance().quit) 161 aboutAct.triggered.connect(self.about) 162 aboutQtAct.triggered.connect(QApplication.instance().aboutQt) 163 164 fileMenu = self.menuBar().addMenu("File") 165 fileMenu.addAction(exitAction) 166 167 helpMenu = self.menuBar().addMenu("About") 168 helpMenu.addAction(aboutAct) 169 helpMenu.addAction(aboutQtAct) 170 171 def modelFromFile(self, fileName): 172 f = QFile(fileName) 173 if not f.open(QFile.ReadOnly): 174 return QStringListModel(self.completer) 175 176 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) 177 178 words = [] 179 while not f.atEnd(): 180 line = f.readLine().trimmed() 181 if line.length() != 0: 182 try: 183 line = str(line, encoding='ascii') 184 except TypeError: 185 line = str(line) 186 187 words.append(line) 188 189 QApplication.restoreOverrideCursor() 190 191 return QStringListModel(words, self.completer) 192 193 def about(self): 194 QMessageBox.about(self, "About", 195 "This example demonstrates the different features of the " 196 "QCompleter class.") 197 198 199if __name__ == '__main__': 200 201 import sys 202 203 app = QApplication(sys.argv) 204 window = MainWindow() 205 window.show() 206 sys.exit(app.exec_()) 207