QT:自动补全(QCompleter或QListView)

来源:互联网 发布:2016安全事故数据统计 编辑:程序博客网 时间:2024/05/16 14:59

一、简介

       QCompleter能实现QLineEdit根据输入自动补全的功能,根据单词列表提示完成单词输入,也可补全文件路径。类似于百度,输入关键字列出关联的匹配结果。 不过QCompleter无法自定义匹配规则(只能模糊匹配前N个字符),本文将简要介绍并使用QListView和QStringList组合完成自定义的规则。

二、运行图

(1)运行如下图1所示。


三、详解

1、QCompleter补全路径

(1)区分大小写,补全文件的路径,因焦点一移动弹出的下拉列表就回收,所以无法截图,可自行测试。

[cpp] view plain copy
  1. {      
  2.     QDirModel *model = new QDirModel(this);  
  3.     search_line_edit = new QLineEdit(this);  
  4.     completer = new QCompleter(this);  
  5.     completer->setModel(model);  
  6.     search_line_edit->setCompleter(completer);  
  7. }  

2、QCompleter补全文本

[cpp] view plain copy
  1. #include "widget.h"  
  2.   
  3.   
  4. Widget::Widget(QWidget *parent)  
  5.     : QWidget(parent)  
  6. {  
  7.     word_list<<"Java"<<"C++"<<"C#"<<"PHP"<<"Perl"<<"Python"<<"Delphi"<<"Ruby";  
  8.     search_line_edit = new QLineEdit(this);  
  9.     completer = new QCompleter(this);  
  10.     string_list_model = new QStringListModel(word_list, this);  
  11.     completer->setCaseSensitivity(Qt::CaseInsensitive);  
  12.     completer->setModel(string_list_model);  
  13.     search_line_edit->setCompleter(completer);  
  14.     connect(search_line_edit, SIGNAL(editingFinished()), this, SLOT(editComplete()));  
  15. }  
  16.   
  17. void Widget::editComplete()  
  18. {  
  19.     QString text = search_line_edit->text();  
  20.     if(QString::compare(text, QString("")) != 0) {  
  21.         bool is_contains = word_list.contains(text, Qt::CaseInsensitive);  
  22.         if(!is_contains) {  
  23.            word_list<<text;  
  24.            string_list_model->setStringList(word_list);  
  25.            //completer->setModel(new QStringListModel(wordList, this));  
  26.         }  
  27.     }  
  28. }  
每次编译完成后按回车键,会将不存在列表中的文本加入到改列表中。Qt::CaseSensitivity取值,Qt::CaseInsensitive:大小写不敏感;Qt::CaseSensitive:大小写敏感。默认为:Qt::CaseSensitive。

3、类似QComplater可自定义匹配规则

(1)单击插入列表,可以将新的文本加入到列表中,也可以按回车键(通过安装事件过滤器实现),将新文本加入到列表。

[cpp] view plain copy
  1. connect(button,SIGNAL(clicked()),this,SLOT(addWordToList()));  
  2.   
  3. void Widget::addWordToList()  
  4. {  
  5.     if(edit->text().isEmpty())  
  6.         return;  
  7.     if (!edit->word_list.contains(edit->text())) {  
  8.        edit->word_list << edit->text();  
  9.     }  
  10. }  
  11.   
  12. bool Widget::eventFilter(QObject *obj, QEvent *event)  
  13. {  
  14.   if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::NonClientAreaMouseMove) {  
  15.       QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);  
  16.       if (mouseEvent->buttons() & Qt::LeftButton) {  
  17.           if (obj != edit->listView && obj != edit) {  
  18.               edit->listView->hide();  
  19.           }  
  20.       }  
  21.   }  
  22.   QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);  
  23.   if (Qt::Key_Enter == keyEvent->key() || Qt::Key_Return == keyEvent->key()) {  
  24.       if (!edit->word_list.contains(edit->text())) {  
  25.          edit->word_list << edit->text();  
  26.       }  
  27.   }  
  28.   return QWidget::eventFilter(obj, event);  
  29. }  

(2)变动

改变窗口的位置和大小,列表都要变动,都是通过事件相应在使下拉列表变宽,拉长或隐藏。

[cpp] view plain copy
  1. void Widget::moveEvent(QMoveEvent *event)  
  2. {  
  3.   if (!edit->listView->isHidden()) {  
  4.     edit->setCompleter(edit->text());  
  5.   }  
  6. }  
  7.   
  8. void CompleteLineEdit::resizeEvent(QResizeEvent *event)  
  9. {  
  10.   if (!listView->isHidden()) {  
  11.     setCompleter(this->text());  
  12.   }  
  13.   QLineEdit::resizeEvent(event);  
  14. }  

(4)键盘事件处理

[cpp] view plain copy
  1. void CompleteLineEdit::keyPressEvent(QKeyEvent *e) {  
  2.     if (!listView->isHidden()) {  
  3.         int key = e->key();  
  4.         int count = listView->model()->rowCount();  
  5.         QModelIndex currentIndex = listView->currentIndex();  
  6.         if (Qt::Key_Down == key) {  
  7.             // 按向下方向键时,移动光标选中下一个完成列表中的项  
  8.             int row = currentIndex.row() + 1;  
  9.             if (row >= count) {  
  10.                 row = 0;  
  11.              }  
  12.             QModelIndex index = listView->model()->index(row, 0);  
  13.             listView->setCurrentIndex(index);  
  14.         } else if (Qt::Key_Up == key) {  
  15.             // 按向下方向键时,移动光标选中上一个完成列表中的项  
  16.             int row = currentIndex.row() - 1;  
  17.             if (row < 0) {  
  18.                  row = count - 1;  
  19.             }  
  20.             QModelIndex index = listView->model()->index(row, 0);  
  21.             listView->setCurrentIndex(index);  
  22.         } else if (Qt::Key_Escape == key) {  
  23.             // 按下Esc键时,隐藏完成列表  
  24.             listView->hide();  
  25.         } else if (Qt::Key_Enter == key || Qt::Key_Return == key) {  
  26.             // 按下回车键时,使用完成列表中选中的项,并隐藏完成列表  
  27.             if (currentIndex.isValid()) {  
  28.                 QString text = listView->currentIndex().data().toString();  
  29.                 setText(text);  
  30.             }  
  31.             listView->hide();  
  32.         } else if (Qt::Key_Delete == key) {  
  33.             QModelIndexList indexList = listView->selectionModel()->selectedRows();  
  34.             QModelIndex index;  
  35.             QString str;  
  36.             int i = 0;  
  37.             foreach(index, indexList) {  
  38.                 str = index.data().toString();  
  39.                 this->model->removeRow(index.row() - i);  
  40.                 word_list.removeAll(str);  
  41.                 ++i;  
  42.             }  
  43.         } else {  
  44.         // 其他情况,隐藏完成列表,并使用QLineEdit的键盘按下事件  
  45.             listView->hide();  
  46.             QLineEdit::keyPressEvent(e);  
  47.         }  
  48.     } else {  
  49.         QLineEdit::keyPressEvent(e);  
  50.     }  
  51. }  
(5)匹配方式

[cpp] view plain copy
  1. void CompleteLineEdit::setCompleter(const QString &text) {  
  2.     if (text.isEmpty()) {  
  3.         listView->hide();  
  4.         return;  
  5.      }  
  6.     //if ((text.length() > 1) && (!listView->isHidden()))  return;  
  7.   
  8.     // 如果完整的完成列表中的某个单词包含输入的文本,则加入要显示的完成列表串中  
  9.     QStringList tempStr;  
  10.     /*********匹配方式一*************/  
  11. //    foreach(QString word, word_list) {  
  12. //        if (word.contains(text, Qt::CaseInsensitive)) {  
  13. //          tempStr << word;  
  14. //        }  
  15. //    }  
  16.     /*********匹配方式二*************/  
  17.     foreach(QString word, word_list) {  
  18.         if (word.startsWith(text, Qt::CaseInsensitive)) {  
  19.             tempStr << word;  
  20.         }  
  21.     }  
  22.   
  23.     model->setStringList(tempStr);  
  24.     listView->setModel(model);  
  25. // ...........  
  26. }  
      自定义匹配方式,可以startsWith定义为字符串是否与列表字符串的开头大小写匹配,也可以contains定义为列表字符串的包含匹配,还可以endsWith定义为字符串是否与列表字符串的结尾大小写匹配。总之根据需要完成规则匹配。

4、其他设计参考

       可以设置为setWindowFlags(Qt::Popup),然后通过安装事件过滤器处理所有的事件请求。作为参考扩展知识点使用,下面的代码没有控制下拉列表框的高度,没有将新的文本加入到列表中。

[cpp] view plain copy
  1. #include "qfindedit.h"  
  2.   
  3. QFindEdit::QFindEdit(QWidget *parent)  
  4. : QLineEdit(parent)  
  5. , m_bEditFocus(true)  
  6. {  
  7.     this->setWindowTitle("please input find word");  
  8.     m_stringListmodel = new QStringListModel(this);  
  9.     m_pFindWnd = new QListView(this);  
  10.     m_pFindWnd->setWindowFlags(Qt::Popup);  
  11.     m_pFindWnd->setEditTriggers(QAbstractItemView::NoEditTriggers);  
  12.     m_pFindWnd->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);  
  13.     m_pFindWnd->setSelectionBehavior(QAbstractItemView::SelectRows);  
  14.     m_pFindWnd->setSelectionMode(QAbstractItemView::SingleSelection);  
  15. //  m_pFindWnd->setParent(0, Qt::ToolTip);  
  16.     m_pFindWnd->setFocusPolicy(Qt::NoFocus);  
  17.     m_pFindWnd->setFocusProxy(this);  
  18.       
  19.     m_stringList << "Biao" << "Bin" << "Huang" << "Hua" << "Hello" << "BinBin" << "Hallo";  
  20.   
  21.     connect(this, SIGNAL(textEdited(const QString&)), this, SLOT(textEditedSlot(const QString&)));  
  22.     QObject::connect(m_pFindWnd, SIGNAL(clicked(QModelIndex)),  
  23.         this, SLOT(clickedSlot(QModelIndex)));  
  24.     QObject::connect(this, SIGNAL(activated(QModelIndex)),  
  25.         m_pFindWnd, SLOT(hide()));  
  26.   
  27.     this->installEventFilter(this);  
  28.     m_pFindWnd->installEventFilter(this);  
  29.   
  30. }  
  31.   
  32. QFindEdit::~QFindEdit()  
  33. {  
  34.     delete m_pFindWnd;  
  35. }  
  36.   
  37. QStringList& QFindEdit::stringList()  
  38. {  
  39.     return m_stringList;  
  40. }  
  41.   
  42. void QFindEdit::showFindWnd(const QString& text)  
  43. {  
  44.     //效率较低,需要优化  
  45.     QStringList sl;    
  46.     foreach(QString word, m_stringList) {    
  47.         if (word.contains(text)) {    
  48.             sl << word;   
  49.         }    
  50.     }  
  51.   
  52.     if (sl.size() == 0)   
  53.     {  
  54.         hideFineWnd();  
  55.         return;  
  56.     }  
  57.     m_stringListmodel->setStringList(sl);    
  58.     m_pFindWnd->setModel(m_stringListmodel);    
  59.   
  60.     //高度需要优化  
  61.     m_pFindWnd->resize(rect().width(), 200);  
  62.     QPoint pTopleft = mapToGlobal(rect().bottomLeft());  
  63.     m_pFindWnd->move(pTopleft.x(), pTopleft.y());  
  64.     m_pFindWnd->show();  
  65. }  
  66.   
  67. void QFindEdit::textEditedSlot(const QString& text)  
  68. {  
  69.     QString strText = text.trimmed();  
  70.     if (!strText.isEmpty())  
  71.     {  
  72.         showFindWnd(strText);  
  73.     }  
  74.     else  
  75.     {  
  76.         hideFineWnd();  
  77.     }  
  78. }  
  79.   
  80. void QFindEdit::clickedSlot(QModelIndex modelIndex)  
  81. {  
  82.     setText(m_pFindWnd->model()->data(modelIndex).toString());  
  83.     hideFineWnd();  
  84. }  
  85.   
  86. void QFindEdit::hideFineWnd()  
  87. {  
  88.     m_pFindWnd->hide();  
  89. }  
  90.   
  91. bool QFindEdit::eventFilter(QObject *o, QEvent *e)  
  92. {  
  93.     if (m_bEditFocus && (o == this) && e->type() == QEvent::FocusOut)   
  94.     {  
  95.         if (m_pFindWnd && m_pFindWnd->isVisible())  
  96.             return true;  
  97.     }  
  98.   
  99.     if (o != m_pFindWnd) {  
  100.         return QLineEdit::eventFilter(o, e);  
  101.     }  
  102.   
  103.     switch (e->type())   
  104.     {  
  105.     case QEvent::KeyPress:   
  106.         {  
  107.             QKeyEvent *ke = static_cast<QKeyEvent *>(e);  
  108.             QModelIndex curIndex = m_pFindWnd->currentIndex();  
  109.             QModelIndexList selList = m_pFindWnd->selectionModel()->selectedIndexes();  
  110.             const int key = ke->key();  
  111.   
  112.             if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid() )  
  113.             {  
  114.                 m_pFindWnd->setCurrentIndex(curIndex);  
  115.                 return true;  
  116.             }  
  117.   
  118.             switch (key)   
  119.             {  
  120.             case Qt::Key_End:  
  121.             case Qt::Key_Home:  
  122.                 if (ke->modifiers() & Qt::ControlModifier)  
  123.                     return false;  
  124.                 break;  
  125.   
  126.             case Qt::Key_Up:  
  127.                 if (!curIndex.isValid())   
  128.                 {  
  129.                     int rowCount = m_pFindWnd->model()->rowCount();  
  130.                     QModelIndex lastIndex = m_pFindWnd->model()->index(rowCount - 1, m_pFindWnd->modelColumn());  
  131.                     m_pFindWnd->setCurrentIndex(lastIndex);  
  132.                     return true;  
  133.                 }   
  134.                 else if (curIndex.row() == 0)   
  135.                 {  
  136.                     return true;  
  137.                 }  
  138.                 return false;  
  139.   
  140.             case Qt::Key_Down:  
  141.                 if (!curIndex.isValid())   
  142.                 {  
  143.                     QModelIndex firstIndex = m_pFindWnd->model()->index(0, m_pFindWnd->modelColumn());  
  144.                     m_pFindWnd->setCurrentIndex(firstIndex);  
  145.                     return true;  
  146.                 }   
  147.                 else if (curIndex.row() == m_pFindWnd->model()->rowCount() - 1)   
  148.                 {  
  149.                     return true;  
  150.                 }  
  151.                 return false;  
  152.             }  
  153.   
  154.             m_bEditFocus = false;  
  155.             this->event(ke);  
  156.             m_bEditFocus = true;  
  157.             if ( e->isAccepted() || !m_pFindWnd->isVisible()) {  
  158.                 if (!this->hasFocus())  
  159.                     hideFineWnd();  
  160.                 if (e->isAccepted())  
  161.                     return true;  
  162.             }  
  163.   
  164.             switch (key)   
  165.             {  
  166.             case Qt::Key_Return:  
  167.             case Qt::Key_Enter:  
  168.             case Qt::Key_Tab:  
  169.                 hideFineWnd();   
  170.                 if (curIndex.isValid())   
  171.                 {    
  172.                     QString text = m_pFindWnd->currentIndex().data().toString();    
  173.                     setText(text);    
  174.                 }    
  175.                 break;  
  176.   
  177.             case Qt::Key_F4:  
  178.                 if (ke->modifiers() & Qt::AltModifier)  
  179.                     hideFineWnd();  
  180.                 break;  
  181.   
  182.             case Qt::Key_Backtab:  
  183.             case Qt::Key_Escape:  
  184.                 hideFineWnd();  
  185.                 break;  
  186.   
  187.             default:  
  188.                 break;  
  189.             }  
  190.   
  191.             return true;  
  192.         }  
  193.     case QEvent::MouseButtonPress:   
  194.         if (!m_pFindWnd->underMouse())   
  195.         {  
  196.             hideFineWnd();  
  197.             return true;  
  198.         }  
  199.         return false;  
  200.     case QEvent::InputMethod:  
  201.     case QEvent::ShortcutOverride:  
  202.         QApplication::sendEvent(this, e);  
  203.         break;  
  204.   
  205.     default:  
  206.         return false;  
  207.     }  
  208.     return false;  
  209. }  

四、总结

(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。

原创粉丝点击