一、简介
QCompleter能实现QLineEdit根据输入自动补全的功能,根据单词列表提示完成单词输入,也可补全文件路径。类似于百度,输入关键字列出关联的匹配结果。 不过QCompleter无法自定义匹配规则(只能模糊匹配前N个字符),本文将简要介绍并使用QListView和QStringList组合完成自定义的规则。
二、运行图
(1)运行如下图1所示。
三、详解
1、QCompleter补全路径
(1)区分大小写,补全文件的路径,因焦点一移动弹出的下拉列表就回收,所以无法截图,可自行测试。
- {
- QDirModel *model = new QDirModel(this);
- search_line_edit = new QLineEdit(this);
- completer = new QCompleter(this);
- completer->setModel(model);
- search_line_edit->setCompleter(completer);
- }
2、QCompleter补全文本
- #include "widget.h"
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- {
- word_list<<"Java"<<"C++"<<"C#"<<"PHP"<<"Perl"<<"Python"<<"Delphi"<<"Ruby";
- search_line_edit = new QLineEdit(this);
- completer = new QCompleter(this);
- string_list_model = new QStringListModel(word_list, this);
- completer->setCaseSensitivity(Qt::CaseInsensitive);
- completer->setModel(string_list_model);
- search_line_edit->setCompleter(completer);
- connect(search_line_edit, SIGNAL(editingFinished()), this, SLOT(editComplete()));
- }
- void Widget::editComplete()
- {
- QString text = search_line_edit->text();
- if(QString::compare(text, QString("")) != 0) {
- bool is_contains = word_list.contains(text, Qt::CaseInsensitive);
- if(!is_contains) {
- word_list<<text;
- string_list_model->setStringList(word_list);
- //completer->setModel(new QStringListModel(wordList, this));
- }
- }
- }
3、类似QComplater可自定义匹配规则
(1)单击插入列表,可以将新的文本加入到列表中,也可以按回车键(通过安装事件过滤器实现),将新文本加入到列表。
- connect(button,SIGNAL(clicked()),this,SLOT(addWordToList()));
- void Widget::addWordToList()
- {
- if(edit->text().isEmpty())
- return;
- if (!edit->word_list.contains(edit->text())) {
- edit->word_list << edit->text();
- }
- }
- bool Widget::eventFilter(QObject *obj, QEvent *event)
- {
- if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::NonClientAreaMouseMove) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- if (mouseEvent->buttons() & Qt::LeftButton) {
- if (obj != edit->listView && obj != edit) {
- edit->listView->hide();
- }
- }
- }
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
- if (Qt::Key_Enter == keyEvent->key() || Qt::Key_Return == keyEvent->key()) {
- if (!edit->word_list.contains(edit->text())) {
- edit->word_list << edit->text();
- }
- }
- return QWidget::eventFilter(obj, event);
- }
(2)变动
改变窗口的位置和大小,列表都要变动,都是通过事件相应在使下拉列表变宽,拉长或隐藏。
- void Widget::moveEvent(QMoveEvent *event)
- {
- if (!edit->listView->isHidden()) {
- edit->setCompleter(edit->text());
- }
- }
- void CompleteLineEdit::resizeEvent(QResizeEvent *event)
- {
- if (!listView->isHidden()) {
- setCompleter(this->text());
- }
- QLineEdit::resizeEvent(event);
- }
(4)键盘事件处理
- void CompleteLineEdit::keyPressEvent(QKeyEvent *e) {
- if (!listView->isHidden()) {
- int key = e->key();
- int count = listView->model()->rowCount();
- QModelIndex currentIndex = listView->currentIndex();
- if (Qt::Key_Down == key) {
- // 按向下方向键时,移动光标选中下一个完成列表中的项
- int row = currentIndex.row() + 1;
- if (row >= count) {
- row = 0;
- }
- QModelIndex index = listView->model()->index(row, 0);
- listView->setCurrentIndex(index);
- } else if (Qt::Key_Up == key) {
- // 按向下方向键时,移动光标选中上一个完成列表中的项
- int row = currentIndex.row() - 1;
- if (row < 0) {
- row = count - 1;
- }
- QModelIndex index = listView->model()->index(row, 0);
- listView->setCurrentIndex(index);
- } else if (Qt::Key_Escape == key) {
- // 按下Esc键时,隐藏完成列表
- listView->hide();
- } else if (Qt::Key_Enter == key || Qt::Key_Return == key) {
- // 按下回车键时,使用完成列表中选中的项,并隐藏完成列表
- if (currentIndex.isValid()) {
- QString text = listView->currentIndex().data().toString();
- setText(text);
- }
- listView->hide();
- } else if (Qt::Key_Delete == key) {
- QModelIndexList indexList = listView->selectionModel()->selectedRows();
- QModelIndex index;
- QString str;
- int i = 0;
- foreach(index, indexList) {
- str = index.data().toString();
- this->model->removeRow(index.row() - i);
- word_list.removeAll(str);
- ++i;
- }
- } else {
- // 其他情况,隐藏完成列表,并使用QLineEdit的键盘按下事件
- listView->hide();
- QLineEdit::keyPressEvent(e);
- }
- } else {
- QLineEdit::keyPressEvent(e);
- }
- }
- void CompleteLineEdit::setCompleter(const QString &text) {
- if (text.isEmpty()) {
- listView->hide();
- return;
- }
- //if ((text.length() > 1) && (!listView->isHidden())) return;
- // 如果完整的完成列表中的某个单词包含输入的文本,则加入要显示的完成列表串中
- QStringList tempStr;
- /*********匹配方式一*************/
- // foreach(QString word, word_list) {
- // if (word.contains(text, Qt::CaseInsensitive)) {
- // tempStr << word;
- // }
- // }
- /*********匹配方式二*************/
- foreach(QString word, word_list) {
- if (word.startsWith(text, Qt::CaseInsensitive)) {
- tempStr << word;
- }
- }
- model->setStringList(tempStr);
- listView->setModel(model);
- // ...........
- }
4、其他设计参考
可以设置为setWindowFlags(Qt::Popup),然后通过安装事件过滤器处理所有的事件请求。作为参考扩展知识点使用,下面的代码没有控制下拉列表框的高度,没有将新的文本加入到列表中。
- #include "qfindedit.h"
- QFindEdit::QFindEdit(QWidget *parent)
- : QLineEdit(parent)
- , m_bEditFocus(true)
- {
- this->setWindowTitle("please input find word");
- m_stringListmodel = new QStringListModel(this);
- m_pFindWnd = new QListView(this);
- m_pFindWnd->setWindowFlags(Qt::Popup);
- m_pFindWnd->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_pFindWnd->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- m_pFindWnd->setSelectionBehavior(QAbstractItemView::SelectRows);
- m_pFindWnd->setSelectionMode(QAbstractItemView::SingleSelection);
- // m_pFindWnd->setParent(0, Qt::ToolTip);
- m_pFindWnd->setFocusPolicy(Qt::NoFocus);
- m_pFindWnd->setFocusProxy(this);
- m_stringList << "Biao" << "Bin" << "Huang" << "Hua" << "Hello" << "BinBin" << "Hallo";
- connect(this, SIGNAL(textEdited(const QString&)), this, SLOT(textEditedSlot(const QString&)));
- QObject::connect(m_pFindWnd, SIGNAL(clicked(QModelIndex)),
- this, SLOT(clickedSlot(QModelIndex)));
- QObject::connect(this, SIGNAL(activated(QModelIndex)),
- m_pFindWnd, SLOT(hide()));
- this->installEventFilter(this);
- m_pFindWnd->installEventFilter(this);
- }
- QFindEdit::~QFindEdit()
- {
- delete m_pFindWnd;
- }
- QStringList& QFindEdit::stringList()
- {
- return m_stringList;
- }
- void QFindEdit::showFindWnd(const QString& text)
- {
- //效率较低,需要优化
- QStringList sl;
- foreach(QString word, m_stringList) {
- if (word.contains(text)) {
- sl << word;
- }
- }
- if (sl.size() == 0)
- {
- hideFineWnd();
- return;
- }
- m_stringListmodel->setStringList(sl);
- m_pFindWnd->setModel(m_stringListmodel);
- //高度需要优化
- m_pFindWnd->resize(rect().width(), 200);
- QPoint pTopleft = mapToGlobal(rect().bottomLeft());
- m_pFindWnd->move(pTopleft.x(), pTopleft.y());
- m_pFindWnd->show();
- }
- void QFindEdit::textEditedSlot(const QString& text)
- {
- QString strText = text.trimmed();
- if (!strText.isEmpty())
- {
- showFindWnd(strText);
- }
- else
- {
- hideFineWnd();
- }
- }
- void QFindEdit::clickedSlot(QModelIndex modelIndex)
- {
- setText(m_pFindWnd->model()->data(modelIndex).toString());
- hideFineWnd();
- }
- void QFindEdit::hideFineWnd()
- {
- m_pFindWnd->hide();
- }
- bool QFindEdit::eventFilter(QObject *o, QEvent *e)
- {
- if (m_bEditFocus && (o == this) && e->type() == QEvent::FocusOut)
- {
- if (m_pFindWnd && m_pFindWnd->isVisible())
- return true;
- }
- if (o != m_pFindWnd) {
- return QLineEdit::eventFilter(o, e);
- }
- switch (e->type())
- {
- case QEvent::KeyPress:
- {
- QKeyEvent *ke = static_cast<QKeyEvent *>(e);
- QModelIndex curIndex = m_pFindWnd->currentIndex();
- QModelIndexList selList = m_pFindWnd->selectionModel()->selectedIndexes();
- const int key = ke->key();
- if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid() )
- {
- m_pFindWnd->setCurrentIndex(curIndex);
- return true;
- }
- switch (key)
- {
- case Qt::Key_End:
- case Qt::Key_Home:
- if (ke->modifiers() & Qt::ControlModifier)
- return false;
- break;
- case Qt::Key_Up:
- if (!curIndex.isValid())
- {
- int rowCount = m_pFindWnd->model()->rowCount();
- QModelIndex lastIndex = m_pFindWnd->model()->index(rowCount - 1, m_pFindWnd->modelColumn());
- m_pFindWnd->setCurrentIndex(lastIndex);
- return true;
- }
- else if (curIndex.row() == 0)
- {
- return true;
- }
- return false;
- case Qt::Key_Down:
- if (!curIndex.isValid())
- {
- QModelIndex firstIndex = m_pFindWnd->model()->index(0, m_pFindWnd->modelColumn());
- m_pFindWnd->setCurrentIndex(firstIndex);
- return true;
- }
- else if (curIndex.row() == m_pFindWnd->model()->rowCount() - 1)
- {
- return true;
- }
- return false;
- }
- m_bEditFocus = false;
- this->event(ke);
- m_bEditFocus = true;
- if ( e->isAccepted() || !m_pFindWnd->isVisible()) {
- if (!this->hasFocus())
- hideFineWnd();
- if (e->isAccepted())
- return true;
- }
- switch (key)
- {
- case Qt::Key_Return:
- case Qt::Key_Enter:
- case Qt::Key_Tab:
- hideFineWnd();
- if (curIndex.isValid())
- {
- QString text = m_pFindWnd->currentIndex().data().toString();
- setText(text);
- }
- break;
- case Qt::Key_F4:
- if (ke->modifiers() & Qt::AltModifier)
- hideFineWnd();
- break;
- case Qt::Key_Backtab:
- case Qt::Key_Escape:
- hideFineWnd();
- break;
- default:
- break;
- }
- return true;
- }
- case QEvent::MouseButtonPress:
- if (!m_pFindWnd->underMouse())
- {
- hideFineWnd();
- return true;
- }
- return false;
- case QEvent::InputMethod:
- case QEvent::ShortcutOverride:
- QApplication::sendEvent(this, e);
- break;
- default:
- return false;
- }
- return false;
- }
四、总结
(1)上述所有的代码已经打包上传到csdn上可登录下载(http://download.csdn.net/detail/taiyang1987912/7573181)。
(2)一般使用内部的QCompleter均能完成需求,特殊处理时才自定义规则。
(3)3.3中代码拖动窗口时,下拉列表不会隐藏,这个问题暂时解决不了。下拉列表的每一项的高度不知怎么获取,因此下拉列表框度比项的宽度要大。
(4)所用的Qt的库Qt4.6.2,GCC4.4.6 20120305 (Red Hat 4.4.6-4) 。系统是centos6.3。