首頁>Program>source

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

通常的功能是基於前缀提供完成提示(這些提示可以在下拉列表中而不是內聯顯示).例如,给定

chicken soup
chilli peppers
grilled chicken

进入 ch 会匹配 chicken soupchilli peppers 但不是 grilled chicken

我想要的是能够輸入 ch 並匹配所有這些物件,或更具體地,匹配 chicken 並匹配 chicken soupgrilled chicken .
我還希望能够分配像 chs這樣的標簽 到 chicken soup 产生另一个匹配項,而不仅仅是文字內容.我可以處理演算法,但是

我需要重寫QCompleter的哪些功能?
我不確定我應该去哪裏找...

最新回復
  • 5月前
    1 #

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

  • 5月前
    2 #

    使用 filterMode : Qt::MatchFlags 屬性.此屬性儲存如何執行過濾.如果filterMode設置為 Qt::MatchStartsWith ,仅顯示以键入字元開頭的條目. Qt::MatchContains 將顯示包含键入字元的條目,並且 Qt::MatchEndsWith 以键入字元結尾的字元. Currently, only these three modes are implemented .將filterMode設置為任何其他 Qt::MatchFlag 將發出警告,並且將不執行任何操作.預設模式為 Qt::MatchStartsWith

    This property was introduced in Qt 5.2.

    訪問功能:

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

  • 5月前
    3 #

    基於@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 是空的,這反過来又重置了 text 組合框.我試圖找到一个解決此問题的好方法,但是每当我尝試在 self.filterProxyModel上更改某些內容時,我都会遇到問题(引用已删除的物件錯誤) .所以現在的黑客是建立 self.filterProxyModel的模型 每当它的模式更新時都是新的.並且只要该模式不再与模型中的任何內容匹配,請為其提供一个仅包含当前文字的新模型(又名 pathsplitPath ).如果您要處理非常大的模型,這可能会匯致效能問题,但對我而言,這種黑客工具相当不錯。

    更新2:

    我意識到這仍然不是完美的方法,因為如果在組合框中键入了新的字元串,並且使用者按下Enter键,則会再次清除組合框.輸入新字元串的唯一方法是在键入後从下拉選單中選擇它。

    更新3:

    現在輸入作品也是如此.我通過在使用者按下Enter键時簡單地將其取消收费来解決組合框文字的重置問题.但是我將其放迴去,以便保留完成功能.如果使用者決定进行进一步的編輯。

  • 5月前
    4 #

    感谢Thorbjørn, 我確實通過从 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;
    }
    

  • 5月前
    5 #

    很遗憾,当前的答案是不可能.為此,您需要在自己的應用程式中複製QCompleter的许多功能(Qt Creator為它的Locator进行了此操作,請參见 src/plugins/locator/locatorwidget.cpp 如果有兴趣的话,就可以得到魔法。)

    与此同時,您可以對QTBUG-7830进行投票,這將使您可以根据需要自定義完成專案的匹配方式.但是不要屏住呼吸。

  • 如何获取Linux(ubuntu)上的视频捕获設備(網路摄像機)列表? (C / C ++)
  • 如何區分手動滚動(通過滑鼠滚轮/滚動條)和Javascript / jQuery滚動?