如何使用 PyQt5 QCompleter 进行代码补全

How to use PyQt5 QCompleter for code completion

我想创建一个具有基本代码完成功能的 QLineEdit 字段,但到目前为止,每当我 select 一个项目 item.attr 的属性时, item. 是替换为 attr 而不是在 item. 之后插入 attr。此外,如果 attrattr.subattr,则无法预测它,因为 item. 已被替换并且 attr. 在我的模型的根部不存在。

我创建了一个相对最小的示例:

import sys
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication,QWidget,QVBoxLayout,QLineEdit,QCompleter

test_model_data = [
    ('tree',[                           # tree
             ('branch', [               # tree.branch
                         ('leaf',[])]), # tree.branch.leaf
             ('roots',  [])]),          # tree.roots
    ('house',[                          # house
                ('kitchen',[]),         # house.kitchen
                ('bedroom',[])]),       # house.bedroom
    ('obj3',[]),                        # etc..
    ('obj4',[])
]
class codeCompleter(QCompleter):
    def splitPath(self, path):
        return path.split('.') #split table.member

class mainApp(QWidget):
    def __init__(self):
        super().__init__()
        self.entry = QLineEdit(self)
        self.model = QStandardItemModel(parent=self)
        self.completer = codeCompleter(self.model, self)
        self.entry.setCompleter(self.completer)
        layout = QVBoxLayout()
        layout.addWidget(self.entry)
        self.setLayout(layout)

        self.update_model() #normally called from a signal when new data is available

    def update_model(self):
        def addItems(parent, elements):
            for text, children in elements:
                item = QStandardItem(text)
                parent.appendRow(item)
                if children:
                    addItems(item, children)
        addItems(self.model, test_model_data)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    hwind = mainApp()
    hwind.show()
    sys.exit(app.exec_())

我从 Qt5 Docs and an example with Qt4.6 中想到了这种方法,但两者都没有结合我想要完成的所有内容。我需要不同的模型结构吗?我需要 class 更多 QCompleter 吗?我需要一个不同的 Qt class 吗?

gif 示例:(对质量感到抱歉)

结语:

对于那些对实际代码完成感兴趣的人,我在集成@eyllanesc 的答案后扩展了我的代码,以便匹配的标识符序列之前的文本单独保留(匹配序列之前的文本不会阻止匹配,也不会被删除当插入新匹配项时)。只需要一点正则表达式就可以将我们要完成的部分与前面的文本分开:

class CodeCompleter(QCompleter):
    ConcatenationRole = Qt.UserRole + 1
    def __init__(self, parent=None, data=[]):
        super().__init__(parent)
        self.create_model(data)
        self.regex = re.compile('((?:[_a-zA-Z]+\w*)(?:\.[_a-zA-Z]+\w*)*\.?)$')

    def splitPath(self, path): #breaks lineEdit.text() into list of strings to match to model
        match = self.regex.search(path)
        return match[0].split('.') if match else ['']

    def pathFromIndex(self, ix): #gets model node (QStandardItem) and returns "text" for lineEdit.setText(text)
        return self.regex.sub(ix.data(CodeCompleter.ConcatenationRole), self.completionPrefix())

pathFromIndex() 方法 return 将放置在 QLineEdit 中的字符串,而不是 return 项目的文本与其前身文本的连接.为了提高效率并且不计算在线串联,将为包含该数据的模型创建一个新角色。

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QCompleter

test_model_data = [
    ('tree',[                           # tree
             ('branch', [               # tree.branch
                         ('leaf',[])]), # tree.branch.leaf
             ('roots',  [])]),          # tree.roots
    ('house',[                          # house
                ('kitchen',[]),         # house.kitchen
                ('bedroom',[])]),       # house.bedroom
    ('obj3',[]),                        # etc..
    ('obj4',[])
]


class CodeCompleter(QCompleter):
    ConcatenationRole = Qt.UserRole + 1
    def __init__(self, data, parent=None):
        super().__init__(parent)
        self.create_model(data)

    def splitPath(self, path):
        return path.split('.')

    def pathFromIndex(self, ix):
        return ix.data(CodeCompleter.ConcatenationRole)

    def create_model(self, data):
        def addItems(parent, elements, t=""):
            for text, children in elements:
                item = QStandardItem(text)
                data = t + "." + text if t else text
                item.setData(data, CodeCompleter.ConcatenationRole)
                parent.appendRow(item)
                if children:
                    addItems(item, children, data)
        model = QStandardItemModel(self)
        addItems(model, data)
        self.setModel(model)

class mainApp(QWidget):
    def __init__(self):
        super().__init__()
        self.entry = QLineEdit(self)
        self.completer = CodeCompleter(test_model_data, self)
        self.entry.setCompleter(self.completer)
        layout = QVBoxLayout()
        layout.addWidget(self.entry)
        self.setLayout(layout)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    hwind = mainApp()
    hwind.show()
    sys.exit(app.exec_())