Как использовать PyQt5 QCompleter для завершения кода

Я хочу создать QLineEdit поле с базовой возможностью завершения кода, но до сих пор всякий раз, когда я выбираю атрибут элемента item.attr, item. заменяется attr вместо вставки attr после item., Кроме того, если это attr имеет attr.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 и пример с Qt4.6, но ни тот, ни другой не сочетают в себе все, что я пытаюсь достичь. Нужна ли мне другая структура модели? Нужно ли подкласс больше QCompleter? Нужен ли мне другой Qt учебный класс?

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())

1 ответ

Решение

pathFromIndex() Метод возвращает строку, которая будет помещена в QLineEdit, вместо этого он возвратит конкатенацию текста элемента и текстов его предшественников. Чтобы повысить его эффективность и не рассчитывать эту онлайн-конкатенацию, в модели, содержащей эти данные, будет создана новая роль.

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_())
Другие вопросы по тегам