QCompleter自定义完成规则

2024-05-14 06:35:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我用的是Qt4.6,我有一个QComboBox,里面有一个QCompleter。

通常的功能是提供基于前缀的完成提示(这些提示可以在下拉列表中,而不是内联(这是我的用法)。例如,给定

chicken soup
chilli peppers
grilled chicken

输入ch将匹配chicken soupchilli peppers,但不匹配grilled chicken

我想要的是能够输入ch并匹配它们,或者更具体地说,能够输入chicken并匹配chicken soupgrilled chicken
我还希望能够将类似chs的标记分配给chicken soup,以生成另一个不仅仅在文本内容上的匹配。我能处理算法但是

我需要重写QCompleter的哪些函数?
我真的不确定我应该去哪里。。。


Tags: 标记文本功能内容用法列表chsoup
3条回答

使用filterMode : Qt::MatchFlags属性。此属性保存如何执行筛选。如果filterMode设置为Qt::MatchStartsWith,则只显示以键入字符开头的条目。Qt::MatchContains将显示包含类型化字符的条目,以及Qt::MatchEndsWith以类型化字符结尾的条目。目前,仅实现这三种模式。将filterMode设置为任何其他Qt::MatchFlag将发出警告,并且不会执行任何操作。默认模式是Qt::MatchStartsWith

此属性在Qt 5.2中引入。

访问功能:

Qt::MatchFlags  filterMode() const
void    setFilterMode(Qt::MatchFlags filterMode)

基于@j3frea建议,下面是一个工作示例(使用PySide)。似乎每次调用splitPath时都需要设置模型(在setModel中设置一次代理不起作用)。

combobox.setEditable(True)
combobox.setInsertPolicy(QComboBox.NoInsert)

class CustomQCompleter(QCompleter):
    def __init__(self, parent=None):
        super(CustomQCompleter, self).__init__(parent)
        self.local_completion_prefix = ""
        self.source_model = None

    def setModel(self, model):
        self.source_model = model
        super(CustomQCompleter, self).setModel(self.source_model)

    def updateModel(self):
        local_completion_prefix = self.local_completion_prefix
        class InnerProxyModel(QSortFilterProxyModel):
            def filterAcceptsRow(self, sourceRow, sourceParent):
                index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
                return local_completion_prefix.lower() in self.sourceModel().data(index0).lower()
        proxy_model = InnerProxyModel()
        proxy_model.setSourceModel(self.source_model)
        super(CustomQCompleter, self).setModel(proxy_model)

    def splitPath(self, path):
        self.local_completion_prefix = path
        self.updateModel()
        return ""


completer = CustomQCompleter(combobox)
completer.setCompletionMode(QCompleter.PopupCompletion)
completer.setModel(combobox.model())

combobox.setCompleter(completer)

基于@Bruno的答案,我使用标准的QSortFilterProxyModel函数setFilterRegExp来更改搜索字符串。这样就不需要再分类了。

它还修复了@Bruno的答案中的一个错误,当输入字符串在键入时被backspace纠正后,由于某些原因,这些建议就消失了。

class CustomQCompleter(QtGui.QCompleter):
    """
    adapted from: http://stackoverflow.com/a/7767999/2156909
    """
    def __init__(self, *args):#parent=None):
        super(CustomQCompleter, self).__init__(*args)
        self.local_completion_prefix = ""
        self.source_model = None
        self.filterProxyModel = QtGui.QSortFilterProxyModel(self)
        self.usingOriginalModel = False

    def setModel(self, model):
        self.source_model = model
        self.filterProxyModel = QtGui.QSortFilterProxyModel(self)
        self.filterProxyModel.setSourceModel(self.source_model)
        super(CustomQCompleter, self).setModel(self.filterProxyModel)
        self.usingOriginalModel = True

    def updateModel(self):
        if not self.usingOriginalModel:
            self.filterProxyModel.setSourceModel(self.source_model)

        pattern = QtCore.QRegExp(self.local_completion_prefix,
                                QtCore.Qt.CaseInsensitive,
                                QtCore.QRegExp.FixedString)

        self.filterProxyModel.setFilterRegExp(pattern)

    def splitPath(self, path):
        self.local_completion_prefix = path
        self.updateModel()
        if self.filterProxyModel.rowCount() == 0:
            self.usingOriginalModel = False
            self.filterProxyModel.setSourceModel(QtGui.QStringListModel([path]))
            return [path]

        return []

class AutoCompleteComboBox(QtGui.QComboBox):
    def __init__(self, *args, **kwargs):
        super(AutoCompleteComboBox, self).__init__(*args, **kwargs)

        self.setEditable(True)
        self.setInsertPolicy(self.NoInsert)

        self.comp = CustomQCompleter(self)
        self.comp.setCompletionMode(QtGui.QCompleter.PopupCompletion)
        self.setCompleter(self.comp)#
        self.setModel(["Lola", "Lila", "Cola", 'Lothian'])

    def setModel(self, strList):
        self.clear()
        self.insertItems(0, strList)
        self.comp.setModel(self.model())

    def focusInEvent(self, event):
        self.clearEditText()
        super(AutoCompleteComboBox, self).focusInEvent(event)

    def keyPressEvent(self, event):
        key = event.key()
        if key == 16777220:
            # Enter (if event.key() == QtCore.Qt.Key_Enter) does not work
            # for some reason

            # make sure that the completer does not set the
            # currentText of the combobox to "" when pressing enter
            text = self.currentText()
            self.setCompleter(None)
            self.setEditText(text)
            self.setCompleter(self.comp)

        return super(AutoCompleteComboBox, self).keyPressEvent(event)

更新:

我认为我以前的解决方案有效,直到组合框中的字符串与列表项都不匹配。然后QFilterProxyModel为空,这又重置了组合框的text。我试图找到一个优雅的解决方案来解决这个问题,但是每当我试图改变self.filterProxyModel上的内容时,我就遇到了问题(引用已删除的对象错误)。所以现在的诀窍是每次更新模式时都设置self.filterProxyModel的模型。当模式不再匹配模型中的任何内容时,给它一个新的模型,它只包含当前文本(也就是path中的splitPath)。如果您处理的是非常大的模型,这可能会导致性能问题,但对我来说,黑客操作非常有效。

更新2:

我意识到这仍然不是一个完美的方法,因为如果在组合框中键入一个新字符串,并且用户按enter,组合框将再次被清除。输入新字符串的唯一方法是在键入后从下拉菜单中选择它。

更新3:

现在输入works。当用户按enter键时,我只需取消对组合框文本的设置。但是我把它放回原处,这样完成功能就可以保持原样了。如果用户决定进行进一步编辑。

相关问题 更多 >