我正在使用Qt4.6,我有一个帶有QCompleter的QComboBox。
通常的功能是基於前缀提供完成提示(這些提示可以在下拉列表中而不是內聯顯示).例如,给定
chicken soup
chilli peppers
grilled chicken
进入
ch
会匹配
chicken soup
和
chilli peppers
但不是
grilled chicken
我想要的是能够輸入
ch
並匹配所有這些物件,或更具體地,匹配
chicken
並匹配
chicken soup
和
grilled chicken
.
我還希望能够分配像
chs
這樣的標簽
到
chicken soup
产生另一个匹配項,而不仅仅是文字內容.我可以處理演算法,但是
我需要重寫QCompleter的哪些功能?
我不確定我應该去哪裏找...
- 5月前1 #
- 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
的模型 每当它的模式更新時都是新的.並且只要该模式不再与模型中的任何內容匹配,請為其提供一个仅包含当前文字的新模型(又名path
在splitPath
).如果您要處理非常大的模型,這可能会匯致效能問题,但對我而言,這種黑客工具相当不錯。更新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进行投票,這將使您可以根据需要自定義完成專案的匹配方式.但是不要屏住呼吸。
相似問題
- visual studio 2012:如何使用windows XP支援,使用靜態MSVC執行時在VS2012下靜態構建Qt 48 / 52?qtvisualstudio2012qt4qt52021-01-07 17:52
- c++:Qt GUI應用程式中的控製台輸出?c++windowsqtqt42020-12-29 07:54
- gdb:無法在windows 10上執行Qt Creator除錯qtgdbqt4qtcreator2020-12-18 21:23
- 了解Qt中的表單佈局機製qtformsqt4qtdesigner2020-12-09 10:55
基於@ j3frea的建議,這是一个有效的示例(使用
PySide
).似乎每次splitPath
都要設置模型 被稱為(在setModel
中設置一次代理 不起作用)。