3

我想创建一个QLineEdit具有基本代码完成功能的字段,但到目前为止,每当我选择一个 item 的属性时item.attr,它item.都会被替换attr而不是插入attrafter 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 Docs和 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())
4

1 回答 1

4

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_())
于 2018-04-19T22:34:26.960 回答