Qt浅谈之十:自动补全(QCompleter或QListView)
2014-06-30 17:19
148 查看
一、简介
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)); } } }每次编译完成后按回车键,会将不存在列表中的文本加入到改列表中。Qt::CaseSensitivity取值,Qt::CaseInsensitive:大小写不敏感;Qt::CaseSensitive:大小写敏感。默认为:Qt::CaseSensitive。
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); } }(5)匹配方式
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); // ........... }自定义匹配方式,可以startsWith定义为字符串是否与列表字符串的开头大小写匹配,也可以contains定义为列表字符串的包含匹配,还可以endsWith定义为字符串是否与列表字符串的结尾大小写匹配。总之根据需要完成规则匹配。
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。
(5)本人思路有限,若有更好的设计建议,也可发邮件沟通,在此先感谢!邮箱地址yang.ao@i-soft.com.cn。
相关文章推荐
- QT:自动补全(QCompleter或QListView)
- Qt自动补全(QCompleter或QListView)
- QT笔记之QLineEdit自动补全以及控件提升
- Qt浅谈之二十App自动重启及关闭子窗口
- Qt自动补全代码快捷键和输入法切换快捷键 的冲突解决 (附:fedora上查询 删除软件命令)
- QT- qss(四)行文本编辑框QLineEdit及自动补全
- windows下用vim实现qt代码的自动补全功能
- QCompleter自动补全
- QT自动补全设置
- VS2008中VA解决QT不能自动补全问题及不识别类的问题
- VS + QT 写代码时自动补全
- VA自动补全QT
- QT输入框自动补全
- VA自动补全QT
- qt Qcompleter自动补全类
- Qt安装后要做的第一件事------Qt自动补全代码快捷键和输入法切换快捷键 的冲突解决 (附:fedora下查询 删除软件命令)
- 【Qt编程】基于Qt的词典开发系列<十四>自动补全功能
- Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全
- Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全
- 【Qt编程】基于Qt的词典开发系列<十四>自动补全功能