QCompleter 自定义完成规则

发布于 2024-10-19 15:40:10 字数 568 浏览 8 评论 0原文

我正在使用 Qt4.6,并且有一个带有 QCompleter 的 QComboBox。

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

chicken soup
chilli peppers
grilled chicken

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

我想要的是能够输入 ch 并匹配所有这些,或者更具体地说,chicken 并匹配 chicken soup烤鸡
我还希望能够将 chs 这样的标签分配给 chicken soup 以产生另一个匹配,而不仅仅是文本内容。我可以处理该算法,但是

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

I'm using Qt4.6 and I have a QComboBox with a QCompleter in it.

The usual functionality is to provide completion hints (these can be in a dropdown rather than inline - which is my usage) based on a prefix. For example, given

chicken soup
chilli peppers
grilled chicken

entering ch would match chicken soup and chilli peppers but not grilled chicken.

What I want is to be able to enter ch and match all of them or, more specifically, chicken and match chicken soup and grilled chicken.
I also want to be able to assign a tag like chs to chicken soup to produce another match which is not just on the text's content. I can handle the algorithm but,

Which of QCompleter's functions do I need to override?
I'm not really sure where I should be looking...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

娇女薄笑 2024-10-26 15:40:10

基于@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)

Based on @j3frea suggestion, here is a working example (using PySide). It appears that the model needs to be set every time splitPath is called (setting the proxy once in setModel doesn't work).

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)
魂归处 2024-10-26 15:40:10

基于@Bruno 的答案,我使用标准的 QSortFilterProxyModel 函数 setFilterRegExp 来更改搜索字符串。通过这种方式,不需要子类化。

它还修复了 @Bruno 的答案中的一个错误,该错误使得一旦输入字符串在键入时使用退格键更正后,建议就会因某些原因消失。

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 为空,这又重置了组合框的文本。我试图找到一个优雅的解决方案来解决这个问题,但每当我尝试更改 self.filterProxyModel 上的某些内容时,我都会遇到问题(引用已删除的对象错误)。所以现在的技巧是在每次更新其模式时设置 self.filterProxyModel 的模型。每当模式不再与模型中的任何内容匹配时,就为其提供一个仅包含当前文本的新模型(也称为 splitPath 中的 path)。如果您处理非常大的模型,这可能会导致性能问题,但对我来说,这个技巧非常有效。

更新 2:

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

更新3:

现在也进入作品了。我通过在用户按 Enter 键时将其关闭来解决组合框文本的重置问题。但我把它放回去,以便完成功能保持不变。如果用户决定进行进一步编辑。

Building on the answer of @Bruno, I am using the standard QSortFilterProxyModel function setFilterRegExp to change the search string. In this way no sub-classing is necessary.

It also fixes a bug in @Bruno's answer, which made the suggestions vanish for some reasons once the input string got corrected with backspace while typing.

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)

Update:

I figured that my previous solution worked until the string in the combobox matched none of the list items. Then the QFilterProxyModel was empty and this in turn reseted the text of the combobox. I tried to find an elegant solution to this problem, but I ran into problems (referencing deleted object errors) whenever I tried to change something on self.filterProxyModel. So now the hack is to set the model of self.filterProxyModel everytime new when its pattern is updated. And whenever the pattern does not match anything in the model anymore, to give it a new model that just contains the current text (aka path in splitPath). This might lead to performance issues if you are dealing with very large models, but for me the hack works pretty well.

Update 2:

I realized that this is still not the perfect way to go, because if a new string is typed in the combobox and the user presses enter, the combobox is cleared again. The only way to enter a new string is to select it from the drop down menu after typing.

Update 3:

Now enter works as well. I worked around the reset of the combobox text by simply taking it off charge when the user presses enter. But I put it back in, so that the completion functionality remains in place. If the user decides to do further edits.

晚雾 2024-10-26 15:40:10

使用 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)

Use filterMode : Qt::MatchFlags property. This property holds how the filtering is performed. If filterMode is set to Qt::MatchStartsWith, only those entries that start with the typed characters will be displayed. Qt::MatchContains will display the entries that contain the typed characters, and Qt::MatchEndsWith the ones that end with the typed characters. Currently, only these three modes are implemented. Setting filterMode to any other Qt::MatchFlag will issue a warning, and no action will be performed. The default mode is Qt::MatchStartsWith.

This property was introduced in Qt 5.2.

Access functions:

Qt::MatchFlags  filterMode() const
void    setFilterMode(Qt::MatchFlags filterMode)
时光无声 2024-10-26 15:40:10

谢谢托尔比约恩,
实际上,我确实通过继承 QSortFilterProxyModel 解决了这个问题。

必须覆盖 filterAcceptsRow 方法,然后您只需返回 true 或 false,具体取决于您是否希望显示该项目。

此解决方案的问题在于,它仅隐藏列表中的项目,因此您永远无法重新排列它们(这正是我想要给予某些项目优先级的做法)。

[编辑]
我想我会将其放入解决方案中,因为它[基本上]是我最终所做的(因为上述解决方案不够充分)。我使用 http://www.cppblog.com/biao/archive /2009/10/31/99873.html

#include "locationlineedit.h"
#include <QKeyEvent>
#include <QtGui/QListView>
#include <QtGui/QStringListModel>
#include <QDebug>

LocationLineEdit::LocationLineEdit(QStringList *words, QHash<QString, int> *hash, QVector<int> *bookChapterRange, int maxVisibleRows, QWidget *parent)
: QLineEdit(parent), words(**&words), hash(**&hash)
{
listView = new QListView(this);
model = new QStringListModel(this);
listView->setWindowFlags(Qt::ToolTip);

connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(setCompleter(const QString &)));
connect(listView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(completeText(const QModelIndex &)));

this->bookChapterRange = new QVector<int>;
this->bookChapterRange = bookChapterRange;
this->maxVisibleRows = &maxVisibleRows;

listView->setModel(model);
}

void LocationLineEdit::focusOutEvent(QFocusEvent *e)
{
listView->hide();
QLineEdit::focusOutEvent(e);
}
void LocationLineEdit::keyPressEvent(QKeyEvent *e)
{
int key = e->key();
if (!listView->isHidden())
{
    int count = listView->model()->rowCount();
    QModelIndex currentIndex = listView->currentIndex();

    if (key == Qt::Key_Down || key == Qt::Key_Up)
    {
    int row = currentIndex.row();
    switch(key) {
    case Qt::Key_Down:
        if (++row >= count)
        row = 0;
        break;
    case Qt::Key_Up:
        if (--row < 0)
        row = count - 1;
        break;
    }

    if (listView->isEnabled())
    {
        QModelIndex index = listView->model()->index(row, 0);
        listView->setCurrentIndex(index);
    }
    }
    else if ((Qt::Key_Enter == key || Qt::Key_Return == key || Qt::Key_Space == key) && listView->isEnabled())
    {
    if (currentIndex.isValid())
    {
        QString text = currentIndex.data().toString();
        setText(text + " ");
        listView->hide();
        setCompleter(this->text());
    }
    else if (this->text().length() > 1)
    {
        QString text = model->stringList().at(0);
        setText(text + " ");
        listView->hide();
        setCompleter(this->text());
    }
    else
    {
        QLineEdit::keyPressEvent(e);
    }
    }
    else if (Qt::Key_Escape == key)
    {
    listView->hide();
    }
    else
    {
    listView->hide();
    QLineEdit::keyPressEvent(e);
    }
}
else
{
    if (key == Qt::Key_Down || key == Qt::Key_Up)
    {
    setCompleter(this->text());

    if (!listView->isHidden())
    {
        int row;
        switch(key) {
        case Qt::Key_Down:
        row = 0;
        break;
        case Qt::Key_Up:
        row = listView->model()->rowCount() - 1;
        break;
        }
        if (listView->isEnabled())
        {
        QModelIndex index = listView->model()->index(row, 0);
        listView->setCurrentIndex(index);
        }
    }
    }
    else
    {
    QLineEdit::keyPressEvent(e);
    }
}
}

void LocationLineEdit::setCompleter(const QString &text)
{
if (text.isEmpty())
{
    listView->hide();
    return;
}
/*
This is there in the original but it seems to be bad for performance
(keeping listview hidden unnecessarily - havn't thought about it properly though)
*/
//    if ((text.length() > 1) && (!listView->isHidden()))
//    {
//        return;
//    }


model->setStringList(filteredModelFromText(text));


if (model->rowCount() == 0)
{
    return;
}

int maxVisibleRows = 10;
// Position the text edit
QPoint p(0, height());
int x = mapToGlobal(p).x();
int y = mapToGlobal(p).y() + 1;
listView->move(x, y);
listView->setMinimumWidth(width());
listView->setMaximumWidth(width());
if (model->rowCount() > maxVisibleRows)
{
    listView->setFixedHeight(maxVisibleRows * (listView->fontMetrics().height() + 2) + 2);
}
else
{
    listView->setFixedHeight(model->rowCount() * (listView->fontMetrics().height() + 2) + 2);
}
listView->show();
}

//Basically just a slot to connect to the listView's click event
void LocationLineEdit::completeText(const QModelIndex &index)
{
QString text = index.data().toString();
setText(text);
listView->hide();
}

QStringList LocationLineEdit::filteredModelFromText(const QString &text)
{
QStringList newFilteredModel;

    //do whatever you like and fill the filteredModel

return newFilteredModel;
}

Thanks Thorbjørn,
I actually did solve the problem by inheriting from QSortFilterProxyModel.

The filterAcceptsRow method must be overwritten and then you just return true or false depending on whether or not you want that item displayed.

The problem with this solution is that it only hides items in a list and so you can never rearrange them (which is what I wanted to do to give certain items priority).

[EDIT]
I thought I'd throw this into the solution since it's [basically] what I ended up doing (because the above solution wasn't adequate). I used http://www.cppblog.com/biao/archive/2009/10/31/99873.html:

#include "locationlineedit.h"
#include <QKeyEvent>
#include <QtGui/QListView>
#include <QtGui/QStringListModel>
#include <QDebug>

LocationLineEdit::LocationLineEdit(QStringList *words, QHash<QString, int> *hash, QVector<int> *bookChapterRange, int maxVisibleRows, QWidget *parent)
: QLineEdit(parent), words(**&words), hash(**&hash)
{
listView = new QListView(this);
model = new QStringListModel(this);
listView->setWindowFlags(Qt::ToolTip);

connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(setCompleter(const QString &)));
connect(listView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(completeText(const QModelIndex &)));

this->bookChapterRange = new QVector<int>;
this->bookChapterRange = bookChapterRange;
this->maxVisibleRows = &maxVisibleRows;

listView->setModel(model);
}

void LocationLineEdit::focusOutEvent(QFocusEvent *e)
{
listView->hide();
QLineEdit::focusOutEvent(e);
}
void LocationLineEdit::keyPressEvent(QKeyEvent *e)
{
int key = e->key();
if (!listView->isHidden())
{
    int count = listView->model()->rowCount();
    QModelIndex currentIndex = listView->currentIndex();

    if (key == Qt::Key_Down || key == Qt::Key_Up)
    {
    int row = currentIndex.row();
    switch(key) {
    case Qt::Key_Down:
        if (++row >= count)
        row = 0;
        break;
    case Qt::Key_Up:
        if (--row < 0)
        row = count - 1;
        break;
    }

    if (listView->isEnabled())
    {
        QModelIndex index = listView->model()->index(row, 0);
        listView->setCurrentIndex(index);
    }
    }
    else if ((Qt::Key_Enter == key || Qt::Key_Return == key || Qt::Key_Space == key) && listView->isEnabled())
    {
    if (currentIndex.isValid())
    {
        QString text = currentIndex.data().toString();
        setText(text + " ");
        listView->hide();
        setCompleter(this->text());
    }
    else if (this->text().length() > 1)
    {
        QString text = model->stringList().at(0);
        setText(text + " ");
        listView->hide();
        setCompleter(this->text());
    }
    else
    {
        QLineEdit::keyPressEvent(e);
    }
    }
    else if (Qt::Key_Escape == key)
    {
    listView->hide();
    }
    else
    {
    listView->hide();
    QLineEdit::keyPressEvent(e);
    }
}
else
{
    if (key == Qt::Key_Down || key == Qt::Key_Up)
    {
    setCompleter(this->text());

    if (!listView->isHidden())
    {
        int row;
        switch(key) {
        case Qt::Key_Down:
        row = 0;
        break;
        case Qt::Key_Up:
        row = listView->model()->rowCount() - 1;
        break;
        }
        if (listView->isEnabled())
        {
        QModelIndex index = listView->model()->index(row, 0);
        listView->setCurrentIndex(index);
        }
    }
    }
    else
    {
    QLineEdit::keyPressEvent(e);
    }
}
}

void LocationLineEdit::setCompleter(const QString &text)
{
if (text.isEmpty())
{
    listView->hide();
    return;
}
/*
This is there in the original but it seems to be bad for performance
(keeping listview hidden unnecessarily - havn't thought about it properly though)
*/
//    if ((text.length() > 1) && (!listView->isHidden()))
//    {
//        return;
//    }


model->setStringList(filteredModelFromText(text));


if (model->rowCount() == 0)
{
    return;
}

int maxVisibleRows = 10;
// Position the text edit
QPoint p(0, height());
int x = mapToGlobal(p).x();
int y = mapToGlobal(p).y() + 1;
listView->move(x, y);
listView->setMinimumWidth(width());
listView->setMaximumWidth(width());
if (model->rowCount() > maxVisibleRows)
{
    listView->setFixedHeight(maxVisibleRows * (listView->fontMetrics().height() + 2) + 2);
}
else
{
    listView->setFixedHeight(model->rowCount() * (listView->fontMetrics().height() + 2) + 2);
}
listView->show();
}

//Basically just a slot to connect to the listView's click event
void LocationLineEdit::completeText(const QModelIndex &index)
{
QString text = index.data().toString();
setText(text);
listView->hide();
}

QStringList LocationLineEdit::filteredModelFromText(const QString &text)
{
QStringList newFilteredModel;

    //do whatever you like and fill the filteredModel

return newFilteredModel;
}
骷髅 2024-10-26 15:40:10

不幸的是,目前的答案是不可能。为此,您需要在您的应用程序中复制 QCompleter 的大部分功能自己的应用程序(Qt Creator 为其定位器执行此操作,如果您感兴趣,请参阅 src/plugins/locator/locatorwidget.cpp 来了解其中的魔力)。

同时您可以对 QTBUG-7830 进行投票,这使得自定义方式成为可能完成项目已匹配,如您所愿。但不要对此屏住呼吸。

Unfortunately, the answer is currently that it's not possible. To do that you'd need to duplicate much of the functionality of QCompleter in your own application (Qt Creator does that for its Locator, see src/plugins/locator/locatorwidget.cpp for the magic if you're interested).

Meanwhile you could vote on QTBUG-7830, which is about making it possible to customize the way completion items are matched, like you want. But don't hold your breath on that one.

鸵鸟症 2024-10-26 15:40:10

使用 PyQt5 最简单的解决方案:

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QCompleter

completer = QCompleter()
completer.setFilterMode(Qt.MatchContains)

Easiest solution with PyQt5 :

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QCompleter

completer = QCompleter()
completer.setFilterMode(Qt.MatchContains)
︶ ̄淡然 2024-10-26 15:40:10

该页面现已被查看超过 14k 次,并被 SO​​ 上的许多其他帖子引用。似乎人们每次调用 splitPath 时都在创建和设置新的代理模型,这是完全没有必要的(对于大型模型来说成本高昂)。我们只需要在setModel中设置一次代理模型即可。

正如@bruno 提到的:

看来每次调用 splitPath 时都需要设置模型(在 setModel 中设置一次代理是行不通的)。

这是因为如果我们不使当前过滤无效,代理模型将不会在内部更新。只需确保代理模型上的任何当前过滤或排序无效,然后您将能够看到更新:

    def splitPath(self, path):
        self.local_completion_prefix = path
        self.proxyModel.invalidateFilter()  # invalidate the current filtering
        self.proxyModel.invalidate()  # or invalidate both filtering and sorting
        return ""

这自 Qt 4.3 起可用,请参阅 https://doc.qt.io/qt-5/qsortfilterproxymodel.html#invalidateFilter

This page now has been viewed over 14k times and referenced by many other posts on SO. It seems that people are creating and setting a new proxy model every time when splitPath is called, which is completely unnecessary (and expensive for large models). We just need to set the proxy model once in setModel.

As @bruno mentioned:

It appears that the model needs to be set every time splitPath is called (setting the proxy once in setModel doesn't work).

That is because if we don't invalidate the current filtering, the proxy model won't update internally. Just make sure to invalidate any current filtering or sorting on the proxy model and then you will be able to see the updates:

    def splitPath(self, path):
        self.local_completion_prefix = path
        self.proxyModel.invalidateFilter()  # invalidate the current filtering
        self.proxyModel.invalidate()  # or invalidate both filtering and sorting
        return ""

This is available since Qt 4.3, see https://doc.qt.io/qt-5/qsortfilterproxymodel.html#invalidateFilter

尬尬 2024-10-26 15:40:10

您可以通过提供自定义角色并完成该角色来解决上面提到的 QTBUG-7830。在该角色的处理程序中,您可以使用技巧让 QCompleter 知道该项目在那里。如果您还在 SortFilterProxy 模型中重写 filterAcceptsRow,这将起作用。

You can get around QTBUG-7830 as mentioned above by providing custom role and making completion on that role. In the handler of that role, you can do the trick to let QCompleter know that item is there. This will work if you also override filterAcceptsRow in your SortFilterProxy model.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文