python pyqt5 双层文本框叠加 实现QTextedit 用灰色字体提示单词 实现单词自动补全功能

概要

因为最近在练习怎么写一个单词翻译app,所以想写一个能实现单词自动补全的文本框,就像图中这种用灰色字体显示可能想要的单词,然后按Tab键补全的功能
在这里插入图片描述
浏览了一下论坛里,看到有大佬的实现了一个类似python idle提示器的效果
用QCompleter的重写来实现

解决方案

改写textedit的tab键功能

class MyTextEdit(QTextEdit):
     ACTabsignal = pyqtSignal(bool)
     def __init__(self, parent=None):
         super().__init__(parent)

     def keyPressEvent(self, event):
         if event.key() == Qt.Key_Tab:
             self.ACTabsignal.emit(True)
         else:
             # 传递其他按键事件给父类处理
             super().keyPressEvent(event)

双层edit叠加

用两个textedit,一个透明的书写edit覆盖一个提示edit,再用绝对位置重叠来达到上图的效果

# 创建第一个 QTextEdit 部件
textEdit1 = self.MyTextEdit(self)
textEdit1.setReadOnly(True)
textEdit1.setStyleSheet("border: 1px solid white; border-radius: 5px;color: gray;")
textEdit1.resize(400, 300)
textEdit1.move(0, 0)  # 设置部件的位置
# 创建第二个 QTextEdit 部件
textEdit2 = self.MyTextEdit(self)
textEdit2.setStyleSheet("background: transparent;border: 1px solid white;border-radius: 5px;")
textEdit2.textChanged.connect(lambda: createAutoCompleter(textEdit2.toPlainText()))
        textEdit2.ACTabsignal.connect(lambda: Edit2ToEdit1())
textEdit2.resize(400, 300)
textEdit2.move(0, 0)  # 设置部件的位置,与第一个部件重叠

单词补全功能

接着就是处理补全逻辑,因为涉及到耗时的查找单词补全功能,所以要添加子线程

    class AutoCompleter(QThread):
        SetText = pyqtSignal(str)
        def __init__(self, Editdata,parent=None):
            super().__init__(parent)
            self.Editdata = Editdata
            self.completerList = ["Apple", "Banana",'baby',"Orange", "Pineapple", "Grapes"]

        def run(self):
            # 如果输入为空,则不进行补全
            if not self.Editdata or self.Editdata.isspace():
                self.SetText.emit("")
                return
            # 截取最后一个单词
            web = self.Editdata.split()[-1]
            # 如果不是英文单词,则不进行补全
            if not web.isalpha():
                self.SetText.emit("")
                return
            # 如果末尾单词长度小于 2 则不进行补全
            if len(web) < 2:
                self.SetText.emit("")
                return
            # 遍历所有单词,找到以输入字符开头的单词
            for word in self.completerList:
                if word.lower().startswith(web.lower()):
                    # 保持原有输入字符,并在后面添加补全单词
                    self.SetText.emit(self.Editdata + word[len(web):])
                    return
                # 如果没有找到,则不进行补全
                self.SetText.emit("")

最后别忘了加上信号槽

textEdit2.textChanged.connect(lambda: createAutoCompleter(textEdit2.toPlainText()))
textEdit2.ACTabsignal.connect(lambda: Edit2ToEdit1())

完整代码

import sys

from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QVBoxLayout
from PyQt5.QtCore import QThread, pyqtSignal, Qt



class AutoCompleterEdit(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    class AutoCompleter(QThread):
        SetText = pyqtSignal(str)
        def __init__(self, Editdata,parent=None):
            super().__init__(parent)
            # 截取最后一个单词
            self.Editdata = Editdata
            self.completerList = ["Apple", "Banana",'baby',"Orange", "Pineapple", "Grapes"]

        def run(self):
            # 如果输入为空,则不进行补全
            if not self.Editdata or self.Editdata.isspace():
                self.SetText.emit("")
                return
            web = self.Editdata.split()[-1]
            # 如果不是英文单词,则不进行补全
            if not web.isalpha():
                self.SetText.emit("")
                return
            # 如果末尾单词长度小于 2 则不进行补全
            if len(web) < 2:
                self.SetText.emit("")
                return
            # 遍历所有单词,找到以输入字符开头的单词
            for word in self.completerList:
                if word.lower().startswith(web.lower()):
                    # 保持原有输入字符,并在后面添加补全单词
                    self.SetText.emit(self.Editdata + word[len(web):])
                    return
                # 如果没有找到,则不进行补全
                self.SetText.emit("")

    class MyTextEdit(QTextEdit):
        ACTabsignal = pyqtSignal(bool)
        def __init__(self, parent=None):
            super().__init__(parent)

        def keyPressEvent(self, event):
            if event.key() == Qt.Key_Tab:
                self.ACTabsignal.emit(True)
            else:
                # 传递其他按键事件给父类处理
                super().keyPressEvent(event)

    def initUI(self):
        # 创建自动补全线程
        def createAutoCompleter(text):
            self.ACThread = self.AutoCompleter(text)
            self.ACThread.SetText.connect(textEdit1.setText)
            self.ACThread.start()

        def Edit2ToEdit1():
            textEdit2.setText(textEdit1.toPlainText())
            # 重置第二个编辑框的光标位置
            cursor = textEdit2.textCursor()
            cursor.movePosition(QTextCursor.End)
            textEdit2.setTextCursor(cursor)


        # 创建第一个 QTextEdit 部件
        textEdit1 = self.MyTextEdit(self)
        textEdit1.setReadOnly(True)
        textEdit1.setStyleSheet("border: 1px solid white; border-radius: 5px;color: gray;")
        textEdit1.resize(400, 300)
        textEdit1.move(0, 0)  # 设置部件的位置
        # 创建第二个 QTextEdit 部件
        textEdit2 = self.MyTextEdit(self)
        textEdit2.setStyleSheet("background: transparent;border: 1px solid white;border-radius: 5px;")
        textEdit2.textChanged.connect(lambda: createAutoCompleter(textEdit2.toPlainText()))
        textEdit2.ACTabsignal.connect(lambda: Edit2ToEdit1())
        textEdit2.resize(400, 300)
        textEdit2.move(0, 0)  # 设置部件的位置,与第一个部件重叠

class example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()


    def initUI(self):
        self.setAutoFillBackground(True)
        self.setWindowTitle("AutoCompleterEdit")
        self.resize(400, 300)
        self.vBox = QVBoxLayout()
        ACEdit = AutoCompleterEdit()
        self.vBox.addWidget(ACEdit)
        self.setLayout(self.vBox)
        self.show()

if __name__ == '__main__':
    QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    app = QApplication(sys.argv)
    ex = example()
    sys.exit(app.exec_())

效果展示

在这里插入图片描述

小结

这样的话,只要提供相应词库就能补全末尾单词。但美中不足的是,如果要回到文中修改单词就没办法提供自动补全了,存在一些瑕疵。不过应该可以通过重写处理逻辑,比如对光标位置的判断来提供不同的补全方案进行优化。
最后,如果这篇文章对你有帮助,请帮忙收个藏点个赞吧。

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值