python pyqt5 QTextEdit 2.文本编辑框提示词自动补全,使用QCompleter

1.输入至少2个字符开始提示,按回车自动补全

2.不区分大小写

3.使用替换,防止补全大小写不一致问题

效果图

代码:

1.重写QTextEdit,创建TextEdit.py


from PyQt5.QtCore import Qt,  QStringListModel
from PyQt5.QtGui import  QTextCursor
from PyQt5.QtWidgets import QTextEdit, QCompleter


class TextEdit(QTextEdit):
    def __init__(self, searchWords_dict=dict, matchWords_list=list, parent=None):
        super(TextEdit, self).__init__(parent)

        self.textChangedSign = True
        self.textChanged.connect(self.dealTextChanged)
        self._completer = None
        self.searchWords_dict = searchWords_dict
        self.matchWords_list = matchWords_list
        # print(list(searchWords_dict.keys()))
        # 将传入的搜索词语匹配词整合 , 成为自动匹配选词列表
        self.initAutoCompleteWords()
        # 用户已经完成的前缀
        self.completion_prefix = ''

    def initAutoCompleteWords(self):
        '''
        处理autoCompleteWords_list,以及specialCursorDict
        '''
        self.autoCompleteWords_list = []
        self.specialCursorDict = {}  # 记录待选值的特殊光标位置
        for i in self.searchWords_dict:
            if "$CURSON$" in self.searchWords_dict[i]:
                cursorPosition = len(self.searchWords_dict[i]) - len("$CURSON$") - self.searchWords_dict[i].find(
                    "$CURSON$")
                self.searchWords_dict[i] = self.searchWords_dict[i].replace("$CURSON$", '')
                self.specialCursorDict[i] = cursorPosition
        for i in self.matchWords_list:
            if "$CURSON$" in i:
                cursorPosition = len(i) - len("$CURSON$") - i.find("$CURSON$")
                self.matchWords_list[self.matchWords_list.index(i)] = i.replace("$CURSON$", '')
                self.specialCursorDict[i.replace("$CURSON$", '')] = cursorPosition

        self.autoCompleteWords_list = list(self.searchWords_dict.keys()) + self.matchWords_list

    def setCompleter(self, c):
        self._completer = c
        c.setWidget(self)  # 设置Qcomplete 要关联的窗口小部件。在QLineEdit上设置QCompleter时,会自动调用此函数。在为自定义小部件提供Qcomplete时,需要手动调用。
        # 更改样式
        c.popup().setStyleSheet('''
        QListView {
            #color: #9C9C9C;
            #background-color: #4F4F4F;
        }
        QListView::item:selected //选中项
        {
            background-color: #9C9C9C;
            border: 20px solid #9C9C9C;
        }
        QListView::item:selected:active //选中并处于激活状态时
        {
            background-color: #9C9C9C;
            border: 20px solid #9C9C9C;
        }

        QListView::item {
            color: red;
            padding-top: 5px;
            padding-bottom: 5px;
        }

        QListView::item:hover {
            background-color: #9C9C9C;
        }
        QScrollBar::handle:vertical{ //滑块属性设置
            background:#4F4F4F;
            width:2px;
            height:9px;
            border: 0px;
            border-radius:100px;
            }
        QScrollBar::handle:vertical:normal{
            background-color:#4F4F4F;
            width:2px;
            height:9px;
            border: 0px;
            border-radius:100px;
            }
        QScrollBar::handle:vertical:hover{
            background:#E6E6E6;
            width:2px;
            height:9px;
            border: 0px solid #E5E5E5;
            border-radius:100px;
            }
        QScrollBar::handle:vertical:pressed{
            background:#CCCCCC;
            width:2px;
            height:9px;
            border: 0px solid #E5E5E5;
            border-radius:100px;
            }
        ''')

        '''下面是completer的一些属性设置,可以自行修改'''
        # setModelSorting此方法指定模型中项目的排序方式。值为枚举值
        # 分别为QCompleter.UnsortedModel QCompleter.CaseSensitivelySortedModel QCompleter.CaseInsensitivelySortedModel
        # 内容                                      值     描述
        # QCompleter.UnsortedModel                 0  该模型是未排序的。
        # QCompleter.CaseSensitivelySortedModel    1  该模型是大小写敏感的。
        # QCompleter.CaseInsensitivelySortedModel  2  模型不区分大小写。
        self._completer.setModelSorting(QCompleter.CaseSensitivelySortedModel)
        # self.completer.setCaseSensitivity 和 上述 self.completer.setModelSorting 一样 ,同时设置且不对应时setCaseSensitivity不生效
        # Qt.CaseSensitivity 该属性保持匹配的大小写敏感性。
        # self.completer.setCaseSensitivity(Qt.CaseInsensitive)

        # 如果filterMode设置为Qt.MatchStartsWith,则只会显示以类型化字符开头的条目。 Qt.MatchContains将显示包含类型字符的条目,并且Qt.MatchEnds以类型字符结尾的条目显示。
        # 目前,只有这三种模式得以实施。 将filterMode设置为任何其他Qt :: MatchFlag将发出警告,并且不会执行任何操作。
        # 默认模式是Qt :: MatchStartsWith。
        # 这个属性是在Qt 5.2中引入的。
        self._completer.setFilterMode(Qt.MatchContains)

        # 此属性在导航项目时包含完成项。默认True 这个属性是在Qt 4.3中引入的。
        self._completer.setWrapAround(False)

        # 设置补全模式  有三种枚举值: QCompleter.PopupCompletion(默认)  QCompleter.InlineCompletion   QCompleter.UnfilteredPopupCompletion
        # QCompleter.PopupCompletion(默认) 当前完成显示在一个弹出窗口中。
        # QCompleter.InlineCompletion  完成内联显示(作为选定的文本)。
        # QCompleter.UnfilteredPopupCompletion 所有可能的完成都显示在弹出窗口中,最有可能的建议显示为当前。
        c.setCompletionMode(QCompleter.PopupCompletion)

        # QCompleter.setModel(QAbstractItemModel *model)
        # 设置为模型提供QCompleter的模型。 该模型可以是列表模型或树模型。 如果一个模型已经被预先设置好了,并且它有QCompleter作为它的父项,它将被删除。
        # self.completer.setModel(completer_model)
        self._completer.setModel(QStringListModel(self.autoCompleteWords_list, self._completer))

        '''
        当用户激活popup()中的项目时发送此信号。 (通过点击或按回车)
        QCompleter.activated;如果文本框的当前项目发生更改,则会发出两个信号currentIndexChanged()
        和activated()。无论以编程方式或通过用户交互完成更改,currentIndexChanged()
        总是被发射,而只有当更改是由用户交互引起时才activated()

        注意: 这里的文本框项目发生改变是指QCompleter的项目添加到文本框造成的改变
        '''
        c.activated.connect(self.insertCompletion)  # 为 用户更改项目文档事件 绑定 函数

    def insertCompletion(self, completion):
        '''
        当用户激活popup()中的项目时调用。(通过点击或按回车)
        @competion::添加 QCompleter
        '''

        if self._completer.widget() is not self:  # 如果没有绑定completer跳过此事件
            return
        '''
        俩种补全方式
        1. 搜索添加 在searchWords_dict键中的对应项,删除用户输入的匹配项,自动补全其键值
        2. 匹配添加 在
        '''
        tc = self.textCursor()
        # 判断是搜索添加 还是 直接匹配添加
        if completion in self.searchWords_dict:  # 搜索添加
            # 删除输入的搜索词
            for i in self._completer.completionPrefix():
                tc.deletePreviousChar()
            # 让光标移到删除后的位置
            self.setTextCursor(tc)

            # 插入对应的键值
            insertText = self.searchWords_dict[completion]
            tc.insertText(insertText)
            # 关闭自动补全选项面板
            self._completer.popup().hide()

        else:  # 直接匹配添加
            # 计算用户输入和匹配项差值,并自动补全
            # extra = len(completion) - len(self._completer.completionPrefix())
            # self._completer.completionPrefix() 为 当前匹配项中,用户已输入的值 , 如输入 ab 匹配到 ABORT 则返回 ab

            # 判断用户输入单词 与 需要补全内容是否一致,一致就不用操作
            # if not self.completion_prefix == completion[-extra:]:
            # 自动补全
            # tc.insertText(completion[-extra:])
            # 关闭自动补全选项面板
            # self._completer.popup().hide()
            # 删除输入并填充关键字(解决大小写差异),先删除输入字符串,然后插入提示词
            for i in self._completer.completionPrefix():
                tc.deletePreviousChar()
            tc.insertText(completion)
            # 关闭自动补全选项面板
            self._completer.popup().hide()
        '''更改光标位置'''
        # 移动光标 ,光标位置枚举值
        # cursor_pos = QTextCursor.NoMove #光标不移动
        # cursor_pos = QTextCursor.Start #文档开头
        # cursor_pos = QTextCursor.End #文档结尾
        # cursor_pos = QTextCursor.Up #上一行
        # cursor_pos = QTextCursor.Down #下一行
        # cursor_pos = QTextCursor.Left #向左移动一字符
        # cursor_pos = QTextCursor.Right #向右移动一字符
        # cursor_pos = QTextCursor.StartOfLine  # 行首
        # cursor_pos = QTextCursor.StartOfBlock #段首
        # cursor_pos = QTextCursor.StartOfWord #单词首
        # cursor_pos = QTextCursor.EndOfLine #行末
        # cursor_pos = QTextCursor.EndOfBlock #段末
        # cursor_pos = QTextCursor.EndOfWord #单词末
        # cursor_pos = QTextCursor.PreviousCharacter #上一个字符
        # cursor_pos = QTextCursor.PreviousBlock #上一个段落
        # cursor_pos = QTextCursor.PreviousWord #上一个单词
        # cursor_pos = QTextCursor.NextCharacter #下一个字符
        # cursor_pos = QTextCursor.NextBlock #下一个段落
        # cursor_pos = QTextCursor.NextWord #下一个单词

        # 判断自动补全后是否需要更改特定光标位置
        tc.movePosition(QTextCursor.EndOfWord)  # 先移动到单词尾部,避免错误
        if completion in self.specialCursorDict.keys():  # 存在特殊位置
            for i in range(self.specialCursorDict[completion]):
                tc.movePosition(QTextCursor.PreviousCharacter)
            self.setTextCursor(tc)
        else:  # 不存在特殊位置,移动到单词末尾
            tc.movePosition(QTextCursor.EndOfWord)
            self.setTextCursor(tc)

    # def focusInEvent(self, e):
    # 当edit获取焦点时激活completer
    # Open the widget where you are at in the edit
    #    if self._completer is not None:
    #        self._completer.setWidget(self)
    #    super(TextEdit, self).focusInEvent(e)

    def focusInEvent(self, e):
        # 当edit获取焦点时激活completer
        # Open the widget where you are at in the edit
        if self._completer is not None:
            self._completer.setWidget(self)
        super(TextEdit, self).focusInEvent(e)

    def dealTextChanged(self):
        '''
        内容改变处理信号处理
        '''

        '''下面是对keyPressEvent面对中文输入法输入时没反应的补充'''
        connect = self.toPlainText()

        class QKeyEvent:
            def key(self=None):
                return 0

            def text(self=None):
                return connect.split('\n')[-1].split(' ')[-1]

            def modifiers(self=None):
                return Qt.AltModifier

        self.keyPressEvent(type('QKeyEvent', (QKeyEvent,), {}))

    def getLastPhrase(self):
        '''
        获取最后一个词组(先以行分割,然后按空格分割词组)
        '''
        # 获取全部文本
        connect = self.toPlainText()
        # print(connect.split('\n')[-1].split(' '))
        lastPhrase = connect.split('\n')[-1].split(' ')[-1]
        return lastPhrase

    def keyPressEvent(self, e):
        '''
        按键按下事件
        @e::<PyQt5.QtGui.QKeyEvent object at 0x000001577FAE8048>
        e.text()输入的文本 , 像换行,tab这样的键是没有文本的
        e.key(),键盘上每个键都对应一个编码 如 换行 16777220,j 74
        '''
        isShortcut = False  # 判断是否是快捷键的标志

        # self._completer.popup().isVisible()) 判断 completer 是否弹出
        if self._completer is not None and self._completer.popup().isVisible():
            # print('Popup is up')
            # The following keys are forwarded by the completer to the widget.
            # 如果键入的是特殊键则忽略这次事件
            if e.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab):
                e.ignore()
                return

        # Ctrl + e 快捷键
        if e.key() == Qt.Key_E and e.modifiers() == Qt.ControlModifier:
            words = self.autoCompleteWords_list
            self._completer.setModel(QStringListModel(words))  # 设置数据
            isShortcut = True
        '''
        if e.key() == Qt.Key_Period:
            #This is how I will do the lookup functionality. Show when period is his, open the list of options.
            self.textCursor().insertText('.')
            self.moveCursor(QtGui.QTextCursor.PreviousWord)
            self.moveCursor(QtGui.QTextCursor.PreviousWord, QtGui.QTextCursor.KeepAnchor)
            dict_key = self.textCursor().selectedText().upper()
            #print('Dict Key' , dict_key)
            self.moveCursor(QtGui.QTextCursor.NextWord)
            self.moveCursor(QtGui.QTextCursor.NextWord)

            #print(dict_key)
            words = self.searchWords_dict[dict_key]
            self._completer.setModel(QStringListModel(words, self._completer))
            isShortcut = True
        '''
        # 当不存在关联的 completer 以及 当前键不是快捷键的时候执行父操作
        if (self._completer is None or not isShortcut) and e.key() != 0:
            # Do not process the shortcut when we have a completer.
            super(TextEdit, self).keyPressEvent(e)

        # 当当不存在关联的 completer 或者 有修饰符(ctrl或shift)和输入字符为空时 , 直接返回不进行任何操作
        ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)  # 是ctrl或shift这样的修饰词
        if self._completer is None or (ctrlOrShift and len(e.text()) == 0):
            return

        # eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="
        eow = ''
        # 有修饰符 但 不是ctrl 或者 shift,例如alt出现 hasModifier 就为True
        hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift  # 判断是否有ctrl与shift外的修饰符

        lastPhrase = self.getLastPhrase()  # 当前出现的单词
        self.completion_prefix = lastPhrase

        # 限制最少输入俩个字符后才进行匹配 , 且不满足条件自动关闭界面
        # 不是快捷键,同时满足(没有修饰符 或者 文本为空 或者 输入字符少于2 或者 输入文本最后以eow中一个字符结尾)
        if not isShortcut and (len(e.text()) == 0 or len(lastPhrase) < 2):
            self._completer.popup().hide()
            return
        # not isShortcut 确保快捷键可以操作显示自动补全窗口
        # if not isShortcut and (hasModifier or len(e.text()) == 0 or len(lastPhrase) < 2 or e.text()[-1] in eow):
        # self._completer.popup().hide()
        # return

        # 选中第一项
        if lastPhrase != self._completer.completionPrefix():
            # Puts the Prefix of the word youre typing into the Prefix
            self._completer.setCompletionPrefix(lastPhrase)
            self._completer.popup().setCurrentIndex(  # QCompleter.popup().setCurrentIndex 设置选中项
                self._completer.completionModel().index(0,
                                                        0))  # QCompleter.completionModel() 返回QCompleter模型。 QCompleter模型是一个只读列表模型,它包含当前QCompleter前缀的所有可能匹配项。

        cr = self.cursorRect()
        # 设置 completer 尺寸
        cr.setWidth(self._completer.popup().sizeHintForColumn(
            0) + self._completer.popup().verticalScrollBar().sizeHint().width())
        self._completer.complete(cr)

2.创建TestWindow.py应用

import cgitb
import sys

from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QCompleter, QApplication
from TextEdit import TextEdit


class TestWindow(QMainWindow):
    def __init__(self):
        super(TestWindow, self).__init__(None)
        self.resize(500,400)
        self.completer = QCompleter(self)

        self.searchWords_dict = {
            "插入图片": "![$CURSON$]()",
            "插入链接": "[]()",
        }
        self.matchWords_list = [
            'DEFAULT',
            'DECIMAL',
            'BEGIN',
            'END',
            'IF',
            'ELSEIF',
            'ELSE'
        ]
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)  # 不区分大小写
        self.TextEdit = TextEdit(searchWords_dict=self.searchWords_dict,
                                           matchWords_list=self.matchWords_list,
                                           parent=self)
        self.TextEdit.setCompleter(self.completer)
        self.TextEdit.resize(500, 400)


if __name__ == "__main__":
    def except_hook(cls, exception, traceback):
        sys.__excepthook__(cls, exception, traceback)


    cgitb.enable(format='text')
    sys.excepthook = except_hook
    App = QApplication(sys.argv)  # 创建QApplication对象,作为GUI主程序入口
    mainWindow = TestWindow()
    mainWindow.show()
    sys.exit(App.exec_())  # 循环中等待退出程序

使用说明

1.TextEdit中限制至少输入2个字符才开始提示你(可以在后面自行更改)
2.searchWords_dict参数需要一个字典对象 , 它的作用是为CodeTextEdit添加快捷输入搜索词 . 例如上述示例中插入图片": "![$CURSON$]()即代表输入插入则会出现提示![]()供选择 , 那么$CURSON$的作用呢?它提供一个光标的位置信息,则代表用户选择![]()后光标会移动到[]的中间 , $CURSON$并不是一个必选值 , 如果没有那么光标会出现在最后 .
3.self.matchWords_list 需要一个list对象 , 和searchWords_dict相似 , 'ABORT'的作用是输入AB时自动提示ABORT , 与之对应$CURSON$的作用和用法也相同

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您可以使用 PyQt5 中的 QMessageBox 类来实现点击菜单弹出提示框的功能。以下是一个简单的示例代码: ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, QMessageBox class MyWindow(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # 创建菜单栏 menubar = self.menuBar() # 创建菜单 fileMenu = menubar.addMenu('File') # 创建菜单项 exitAction = QAction('Exit', self) # 绑定功能 exitAction.triggered.connect(self.showDialog) # 将菜单项添加到菜单中 fileMenu.addAction(exitAction) self.setGeometry(300, 300, 350, 250) self.setWindowTitle('Menu Demo') self.show() def showDialog(self): # 弹出提示框 msgBox = QMessageBox() msgBox.setWindowTitle('提示') msgBox.setText('确定要退出吗?') msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) returnValue = msgBox.exec_() if returnValue == QMessageBox.Ok: sys.exit() if __name__ == '__main__': app = QApplication(sys.argv) window = MyWindow() sys.exit(app.exec_()) ``` 在上面的示例代码中,我们先创建了一个菜单栏、一个菜单和一个菜单项,并将菜单项绑定到了 `showDialog()` 方法上。在 `showDialog()` 方法中,我们创建了一个 `QMessageBox` 对象,并设置了提示框的标题和内容。然后,我们通过 `setStandardButtons()` 方法设置了两个标准按钮(确定和取消),并通过 `exec_()` 方法弹出了提示框。最后,根据用户的点击按钮的返回值来决定是否退出程序。 您可以根据自己的需要修改提示框的标题、内容和按钮等属性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值