代码之家  ›  专栏  ›  技术社区  ›  Aaron

如何使用PyQt5 QCompleter完成代码

  •  4
  • Aaron  · 技术社区  · 7 年前

    我想创建一个 QLineEdit 具有基本代码完成功能的字段,但到目前为止,每当我选择项目的属性时 item.attr 这个 item. 替换为 attr 而不是插入 属性 之后 项目 .此外,如果 属性 attr.subattr ,无法预测,因为 项目 已被替换,并且 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 example with Qt4.6 ,但两者都不能将我想要实现的全部目标结合起来。我需要不同的模型结构吗?我是否需要子类化更多 QCompleter ?我需要一个不同的 Qt

    gif示例:(抱歉质量问题)

    enter image description here

    结语:

    对于那些对实际代码完成感兴趣的人,我在集成了@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 回复  |  直到 7 年前
        1
  •  4
  •   Aaron    7 年前

    这个 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_())