自定义QCompleter,直接绑定QLineEdit,可默认选中,自动根据内容缩小窗口,能设置最高显示行数,自动停靠。里面的duplicatelist是本人的特殊用途,正常情况下是不需要,也就构造函数最后一个参数其实没什么用处。代码是在VS2019+Qt6.5.0下写的
刚刚学习Qt三个月,不太熟练,代码有很多地方要优化,请大家多多指教
不说废话了,直接上代码
下面这个是头文件
#pragma once
#include <QListView>
#include <QStringListModel>
#include <QLineEdit>
#include <QCompleter>
#include <QEvent>
#include <QKeyEvent>
#include <QApplication>
class QMyCompleterListView : public QListView {
Q_OBJECT
public:
QMyCompleterListView(QLineEdit* ledit, QStringList startstrlist = { }, QStringList duplicate = { });
~QMyCompleterListView();
//设置要过滤的字符串
void setCompleterStringList(QStringList w) { words = w; };
// 设置要从第几个字符开始显示弹窗,默认是第一位开始显示弹窗
void setStartbit(unsigned int val = 1) { startbit = val ? val : 1; };
//返回默认起始位,要从第几个字符开始显示弹窗
int getStartbit() const { return startbit; };
//设置是否默认选中第一项
//************************************
// 方法: QMyCompleterListView::setStartHighlight
// 权限: public
// 返回类型: void
// 参数: bool hightlight
// 修饰:
// 功能:设置是否默认选中第一项
//************************************
void setStartHighlight(bool hightlight = true);
//返回是否默认选中第一项
//************************************
// 方法: QMyCompleterListView::getStartHighlight
// 权限: public
// 返回类型: bool
// 修饰: const
// 功能:返回是否默认选中第一项
//************************************
bool getStartHighlight() const { return ishightlight; };
//设置模式,model = PopupCompletion,只显示相匹配的文本,UnfilteredPopupCompletion,匹配显示在前,不匹配移动最后
//************************************
// 方法: QMyCompleterListView::setCompleterMode
// 权限: public
// 返回类型: void
// 参数: QCompleter::CompletionMode model
// 修饰:
// 功能:设置显示数据的模式,值是PopupCompletion(0)或UnfilteredPopupCompletion(1)
// PopupCompletion:为只显示匹配字符串;UnfilteredPopupCompletion,显示所有,匹配的在前面。
//************************************
void setCompleterMode(QCompleter::CompletionMode model = QCompleter::PopupCompletion);
//************************************
// 方法: QMyCompleterListView::getCompleterMode
// 权限: public
// 返回类型: QCompleter
// 修饰: const
// 功能:返回显示数据的模式,值是PopupCompletion(0)或UnfilteredPopupCompletion(1)
// PopupCompletion:为只显示匹配字符串;UnfilteredPopupCompletion,显示所有,匹配的在前面。
//************************************
QCompleter::CompletionMode getCompleterMode() const { return completermode; };
//************************************
// 方法: QMyCompleterListView::setCaseSensitive
// 权限: public
// 返回类型: void
// 参数: Qt::CaseSensitivity sensitive
// 修饰:
// 功能: 设置是否区分大小写,值Qt::CaseSensitive或Qt::CaseInsensitive
//************************************
void setCaseSensitive(Qt::CaseSensitivity sensitive = Qt::CaseSensitive);
//************************************
// 方法: QMyCompleterListView::getCaseSensitive
// 权限: public
// 返回类型: Qt::CaseSensitivity
// 修饰: const
// 功能: 返回是否区分大小写,值Qt::CaseSensitive或Qt::CaseInsensitive
//************************************
Qt::CaseSensitivity getCaseSensitive() const { return casesensitive; };
//************************************
// 方法: QMyCompleterListView::getMaxVisibleItems
// 权限: public
// 返回类型: int
// 修饰: const
// 功能: 获取最大显示行数
//************************************
int getMaxVisibleItems() const { return maxvisibleitems; }
//************************************
// 方法: QMyCompleterListView::setMaxVisibleItems
// 权限: public
// 返回类型: void
// 参数: int val
// 修饰:
// 功能:设置最大显示行数
//************************************
void setMaxVisibleItems(int val = 7) { maxvisibleitems = val; };
//************************************
// 方法: QMyCompleterListView::getFiltermode
// 权限: public
// 返回类型: Qt::MatchFlags
// 修饰: const
// 功能:返回过滤模式,值为Qt::MatchContains,任意位置匹配;Qt::MatchStartsWith,只能文本开头匹配
//************************************
Qt::MatchFlags getFiltermode() const { return filtermode; }
//************************************
// 方法: QMyCompleterListView::setFilterMode
// 权限: public
// 返回类型: void
// 参数: Qt::MatchFlags 默认为Qt::MatchStartsWith,只能文本开头匹配
// 修饰:
// 功能:设置过滤模式,值为Qt::MatchContains,任意位置匹配;Qt::MatchStartsWith,只能文本开头匹配
//************************************
void setFilterMode(Qt::MatchFlags fiterm = Qt::MatchStartsWith);
QLineEdit* getLineEdit() const { return lineEdit; }
/* void setLineEdit(QLineEdit* val) { lineEdit = val; }*/
//读取是否显示副本的标志值
bool getIsduplicatelist() const { return isduplicatelist; }
//设置是否显示副本的标志值
void setIsduplicatelist(bool val = false);
//设置显示副本的标志值
void setIsduplicatelist(QStringList duplicate);
private:
QStringList words; // 整个完成列表的单词
QLineEdit* lineEdit;
QStringListModel* model; // 完成列表的model
unsigned int startbit; //此值是设置要从第几个字符开始显示弹出窗
bool ishightlight;//弹窗是否默认第一项
Qt::CaseSensitivity casesensitive; //是否区分大小写
QCompleter::CompletionMode completermode; //补全模式
Qt::MatchFlags filtermode; //过滤模式,开始匹配或任意位置匹配
QStringList duplicatelist; //第二个字符串数组,此数组是副本,往往是为补全字符串的说明,不做为结果
bool isduplicatelist;//是否副本说明
unsigned int maxvisibleitems; //显示窗行数
int x, y;
protected:
QSize sizeHint() const;
bool eventFilter(QObject* watched, QEvent* event);
public slots:
void setCompleter(const QString& text); // 动态的显示完成列表
void completeText(const QModelIndex& index); // 点击完成列表中的项,使用此项自动完成输入的单词
};
cpp文件
#include "QMyCompleterListView.h"
QMyCompleterListView::QMyCompleterListView(QLineEdit* ledit, QStringList startstrlist, QStringList duplicate) : QListView(ledit) {
lineEdit = ledit;
model = new QStringListModel();
if (!startstrlist.isEmpty()) {
setCompleterStringList(startstrlist);
}
if (!duplicate.isEmpty()) {
duplicatelist = duplicate;
}
setWindowFlags(Qt::ToolTip);
//设置Fixed模式,可以使listview能显示最小高度
QSizePolicy policy = sizePolicy();
policy.setVerticalPolicy(QSizePolicy::Fixed);
setSizePolicy(policy);
setEditTriggers(QAbstractItemView::NoEditTriggers);
//setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::SingleSelection);
setFocusPolicy(Qt::NoFocus);
setFocusProxy(lineEdit);
lineEdit->installEventFilter(this);
setMaxVisibleItems();
setStartbit();
setStartHighlight();
setCaseSensitive();
setCompleterMode();
setFilterMode();
setIsduplicatelist();
connect(lineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setCompleter(const QString&)));
connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(completeText(const QModelIndex&)));
}
QMyCompleterListView::~QMyCompleterListView() {
}
void QMyCompleterListView::setStartHighlight(bool hightlight /*= true*/) {
if (hightlight) {
QPalette p = palette();
//QPalette::Inactive 未活动的状态下
p.setColor(QPalette::Inactive, QPalette::Highlight,
p.color(QPalette::Active, QPalette::Highlight));
p.setColor(QPalette::Inactive, QPalette::HighlightedText,
p.color(QPalette::Active, QPalette::HighlightedText));
setPalette(p);
ishightlight = true;
}
else {
QPalette p = palette();
p.setColor(QPalette::Inactive, QPalette::Highlight,
p.color(QPalette::Inactive, QPalette::Highlight));
p.setColor(QPalette::Inactive, QPalette::HighlightedText,
p.color(QPalette::Inactive, QPalette::HighlightedText));
setPalette(p);
ishightlight = false;
}
}
void QMyCompleterListView::setCompleterMode(QCompleter::CompletionMode model /*= QCompleter::PopupCompletion*/) {
completermode = model ? QCompleter::UnfilteredPopupCompletion : QCompleter::PopupCompletion;
}
void QMyCompleterListView::setCaseSensitive(Qt::CaseSensitivity sensitive /*= Qt::CaseSensitive*/) {
casesensitive = sensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
}
void QMyCompleterListView::setFilterMode(Qt::MatchFlags fiterm /*= Qt::MatchStartsWith*/) {
filtermode = fiterm == Qt::MatchContains ? Qt::MatchContains : Qt::MatchStartsWith;
}
void QMyCompleterListView::setIsduplicatelist(bool val /*= false*/) {
if (!val || duplicatelist.isEmpty() || words.isEmpty())
isduplicatelist = false;
else {
QStringList lsstr;
for (int i = 0; i < words.size(); i++) {
lsstr.append(words.at(i) + " " + duplicatelist.at(i));
}
words = lsstr;
isduplicatelist = true;
}
}
void QMyCompleterListView::setIsduplicatelist(QStringList duplicate) {
if (words.isEmpty())
isduplicatelist = false;
else {
duplicatelist = duplicate;
QStringList lsstr;
for (int i = 0; i < words.size(); i++) {
lsstr.append(words.at(i) + " " + duplicatelist.at(i));
}
words = lsstr;
isduplicatelist = true;
}
}
QSize QMyCompleterListView::sizeHint() const {
if (model->rowCount() == 0) return QSize(width(), 0);
int nToShow = maxvisibleitems < model->rowCount() ? maxvisibleitems : model->rowCount();
return QSize(sizeHintForColumn(0) + 10, nToShow * sizeHintForRow(0) + 10);
}
bool QMyCompleterListView::eventFilter(QObject* watched, QEvent* event) {
if (watched == lineEdit && !isHidden()) {
if (event->type() == QEvent::KeyPress) {
if (words.isEmpty()) {
return QWidget::eventFilter(watched, event);
}
int key = ((QKeyEvent*)event)->key();
int count = model->rowCount();
QModelIndex currentIndex = this->currentIndex();
if (Qt::Key_Down == key) {
// 按向下方向键时,移动光标选中下一个完成列表中的项
int row = currentIndex.row() + 1;
if (row >= count) {
row = 0;
}
QModelIndex index = model->index(row, 0);
setCurrentIndex(index);
}
else if (Qt::Key_Up == key) {
// 按向下方向键时,移动光标选中上一个完成列表中的项
int row = currentIndex.row() - 1;
if (row < 0) {
row = count - 1;
}
QModelIndex index = model->index(row, 0);
setCurrentIndex(index);
}
else if (Qt::Key_Escape == key) {
// 按下Esc键时,隐藏完成列表
hide();
}
else if (Qt::Key_Enter == key || Qt::Key_Return == key) {
// 按下回车键时,使用完成列表中选中的项,并隐藏完成列表
if (currentIndex.isValid()) {
QString text = this->currentIndex().data().toString();
if (isduplicatelist)
text = text.left(text.indexOf(" ") + 1);
lineEdit->setText(text);
hide();
}
}
}
else if (event->type() == QEvent::Paint) {
QPoint p(0, lineEdit->height());
if (lineEdit->mapToGlobal(p).x() != x || lineEdit->mapToGlobal(p).y() != y) {
x = lineEdit->mapToGlobal(p).x();
y = lineEdit->mapToGlobal(p).y();
move(x, y);
}
}
else if (event->type() == QEvent::FocusOut) {
hide();
}
}
return QWidget::eventFilter(watched, event);
}
void QMyCompleterListView::setCompleter(const QString& text) {
//字节小于初始位、列表为空或没获得焦点不显示QCompliter窗体。
if (text.size() < startbit || words.isEmpty() || QApplication::focusWidget() != lineEdit) {
hide();
return;
}
if (isduplicatelist && text.indexOf(" ") == text.size() - 1) {
QString str = text;
str.chop(1);
lineEdit->setText(str);
return;
}
hide();
QStringList sl;
QStringList el;
foreach(QString word, words) {
if (getFiltermode() == Qt::MatchStartsWith) {
if (word.startsWith(text, casesensitive)) {
sl << word;
}
else
el << word;
}
else {
if (word.contains(text, casesensitive)) {
sl << word;
}
else
el << word;
}
}
if (completermode == QCompleter::UnfilteredPopupCompletion)
sl << el;
model->setStringList(sl);
setModel(model);
if (model->rowCount() == 0) {
return;
}
setMinimumWidth(lineEdit->width());
setMaximumWidth(sizeHintForColumn(0) + 10);
QPoint p(0, lineEdit->height());
int x = lineEdit->mapToGlobal(p).x();
int y = lineEdit->mapToGlobal(p).y() + 1;
move(x, y);
show();
if (ishightlight && model->rowCount() > 0) {
setCurrentIndex(model->index(0, 0));
}
}
void QMyCompleterListView::completeText(const QModelIndex& index) {
QString text = index.data().toString();
if (isduplicatelist)
text = text.left(text.indexOf(" ") + 1);
lineEdit->setText(text);
hide();
}
实现方法也简单
QStringList word_list;
word_list << "11112 我僮偿" << "11112 大 春偿" << "11113 啊多发点是" << "11114 秦真正的" << "11115 暮云春树地是" << "111在云春树地是" << "1221115 暮大撒反对撒地是" << "11eee15 暮云sdfds地是" << "11ddw 暮云春树地是" << "11asfdasf5 暮云春df sfd是";
QMyCompleterListView* mycompleter = new QMyCompleterListView(ui->LineEdit, word_list);