声明
本篇博客所谓的改进是建立在下面这篇博客代码的基础上。感谢博主 梦醒梦起,希望这篇博客可以帮助大家。
本次代码可以在qt5上运行
https://blog.csdn.net/qq_43793182/article/details/122080407
改进代码
头文件
#pragma once
#include <QComboBox>
#include <QListWidget>
#include <QLineEdit>
#include <QCheckBox>
#include <QEvent>
#include <QMap>
#include <QDebug>
#include <QMouseEvent>
#include <QPoint>
//用户数据结构体,是为了设置每个列表项的信息
struct STRUCT_CK_USER_DATA
{
QString m_userdata;
};
Q_DECLARE_METATYPE(STRUCT_CK_USER_DATA)
//下拉框对勾选框的操作
enum SELECT_TYPE
{
SELECT_TYPE_SEL, //勾选
SELECT_TYPE_REMOVESEL, //去除勾选
};
class MultiSelectComboBox : public QComboBox
{
Q_OBJECT
public:
explicit MultiSelectComboBox(QWidget *parent = Q_NULLPTR);
~MultiSelectComboBox();
/*************************************************
函数名称: addItem
功能描述: 添加一条checkbox选项
参数传入: _text checkbox项显示的文字 _variant用户数据
参数传出:
返回值:
其他:
*************************************************/
void addItem(const QString& _text, const QVariant& _variant = QVariant());
/*************************************************
函数名称: currentText
功能描述: 返回当前选中的checkbox项对应的文本
参数传入:
参数传出:
返回值: QStringList 选中项对应的本文
其他:
*************************************************/
QStringList currentText();
/*************************************************
函数名称: count
功能描述: 返回当前选中的checkbox项的总数
参数传入:
参数传出:
返回值: int 选中的checkbox项的总数
其他:
*************************************************/
int count()const;
/*************************************************
函数名称: SetSearchBarPlaceHolderText
功能描述: 设置搜索文本框默认的显示内容
参数传入: _text 默认显示的文本
参数传出:
返回值:
其他:
*************************************************/
void setSearchBarPlaceHolderText(const QString &_text);
/*************************************************
函数名称: SetPlaceHolderText
功能描述: 设置文本框默认显示的内容
参数传入: _text 默认显示的文本
参数传出:
返回值:
其他:
*************************************************/
void setPlaceHolderText(const QString& _text);
/*************************************************
函数名称: ResetSelection
功能描述: 下拉框中的所有checkbox恢复至不选择状态 而且所对应的用户数据设置为空,同时清空文本显示框
参数传入:
参数传出:
返回值:
其他:
*************************************************/
void resetSelection();
/*************************************************
函数名称: clear
功能描述: 清空下拉框中的所有checkbox控件
参数传入:
参数传出:
返回值:
其他:
*************************************************/
void clearCheckBox();
private:
/*************************************************
函数名称: hidePopup
功能描述: 隐藏MultiSelectComboBox的下拉框界面
参数传入:
参数传出:
返回值:
其他:
*************************************************/
virtual void hidePopup();
virtual void showPopup();
/*************************************************
函数名称: CanHide
功能描述: 根据鼠标当前的坐标位置判断下拉框界面是否可隐藏,如果鼠标处在下拉框界面的内部则不能隐藏,反之可以隐藏
参数传入:
参数传出:
返回值: 不能隐藏(false) 可以隐藏(true)
其他:
*************************************************/
bool CanHide();
protected:
//事件过滤器
virtual bool eventFilter(QObject *watched,QEvent *event);
//滚轮事件
virtual void wheelEvent(QWheelEvent *event);
//按键事件
virtual void keyPressEvent(QKeyEvent *event);
private Q_SLOTS:
/*************************************************
函数名称: stateChange
功能描述: 响应checkbox项的状态变化
参数传入: _state checkbox的状态值
参数传出:
返回值:
其他:
*************************************************/
void stateChange(int _state);
/*************************************************
函数名称: onSearch
功能描述: 响应搜索框内容的变化
参数传入: _text搜索框的内容
参数传出:
返回值:
其他:
*************************************************/
void onSearch(const QString& _text);
//槽函数:点击下拉框选项
/*************************************************
函数名称: itemClicked
功能描述: 响应下拉框选项点击
参数传入: _index 被点击项的索引
参数传出:
返回值:
其他:
*************************************************/
void itemClicked(int _index);
Q_SIGNALS:
/*************************************************
函数名称: SigSelChange
功能描述: 信号:向外界发送数据
参数传入: SELECT_TYPE 勾选框操作类型(是勾选还是去除勾选)
QString 用户数据 这个也可以定结构体
参数传出:
返回值:
其他:
*************************************************/
void SigSelChange(SELECT_TYPE,QString);
private:
//即将要添加的勾选框的索引值 因为下拉界面中可能会增加其他控件,这会对勾选框的索引值产生影响
int m_iCheckBoxNextIndex;
//勾选框起始索引
int m_iCheckBoxStartIndex;
//列表部件(下拉框部件)
QListWidget * m_plistWgt;
//文本框
QLineEdit * m_plineEdt;
//搜索框
QLineEdit * m_plineSer;
//按照点击顺序存储checkbox对象
QVector<QCheckBox *> m_vecSelItem;
};
源文件
#if _MSC_VER >= 1600 //vs2010及其以上版本
#pragma execution_character_set("utf-8") //这句是关键
#endif
#include "CLS_MultiSelectComboBox.h"
MultiSelectComboBox::MultiSelectComboBox(QWidget *parent)
: QComboBox(parent)
, m_iCheckBoxNextIndex(0)
, m_iCheckBoxStartIndex(0)
{
//创建显示文本框界面
m_plineEdt = new QLineEdit(this);
m_plineEdt->setReadOnly(true);
m_plineEdt->installEventFilter(this);
//创建搜索文本框界面
m_plineSer = new QLineEdit(this);
m_plineSer->setPlaceholderText(QStringLiteral("Search........."));
m_plineSer->setClearButtonEnabled(true);
m_plineSer->installEventFilter(this);
//创建下拉列表界面
m_plistWgt = new QListWidget(this);
/*设置搜索框*/
QListWidgetItem* currentItem = new QListWidgetItem(m_plistWgt);
m_plistWgt->addItem(currentItem);
m_plistWgt->setItemWidget(currentItem, m_plineSer);
this->setModel(m_plistWgt->model());
this->setView(m_plistWgt);
this->setLineEdit(m_plineEdt);
this->setMaxVisibleItems(10); //设置可显示项的最大数量
this->installEventFilter(this);
connect(m_plineSer, SIGNAL(textChanged(const QString&)), this, SLOT(onSearch(const QString&)));
connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &MultiSelectComboBox::itemClicked);
//设置样式表
QFile file(QStringLiteral("./style.qss"));
file.open(QIODevice::ReadOnly);
this->setStyleSheet(QString::fromLatin1(file.readAll()));
if(m_plineSer)
{
m_iCheckBoxNextIndex++;
m_iCheckBoxStartIndex++;
}
}
MultiSelectComboBox::~MultiSelectComboBox()
{
}
void MultiSelectComboBox::hidePopup()
{
if(CanHide())
{
QComboBox::hidePopup();
}
}
void MultiSelectComboBox::showPopup()
{
QComboBox::showPopup();
}
void MultiSelectComboBox::addItem(const QString& _text, const QVariant& _variant)
{
if(m_plistWgt == nullptr)
{
return;
}
//新增的列表项对象
QListWidgetItem* item = nullptr;
//获取当前的列表项数
int count = m_plistWgt->count();
if(count == m_iCheckBoxNextIndex)
{//判断下索引和个数的关系是否匹配,注意此时新的checkbox还有添加到列表中
item = new QListWidgetItem(m_plistWgt);
STRUCT_CK_USER_DATA userdata = _variant.value<STRUCT_CK_USER_DATA>();
item->setData(Qt::UserRole + m_iCheckBoxNextIndex,_variant);
}
QCheckBox * checkbox = new QCheckBox(this);
checkbox->setText(_text);
checkbox->installEventFilter(this);
m_plistWgt->addItem(item);
m_plistWgt->setItemWidget(item, checkbox);
m_iCheckBoxNextIndex++;
connect(checkbox, &QCheckBox::stateChanged, this, &MultiSelectComboBox::stateChange);
}
QStringList MultiSelectComboBox::currentText()
{
QStringList text_list;
if(m_plineEdt == nullptr)
{
return text_list;
}
if (!m_plineEdt->text().isEmpty())
{
//以;为分隔符分割字符串
text_list = m_plineEdt->text().split(';');
}
return text_list;
}
int MultiSelectComboBox::count() const
{
if(m_plistWgt == nullptr)
{
return 0;
}
int count = m_plistWgt->count() - m_iCheckBoxStartIndex;
if (count < 0)
{
count = 0;
}
return count;
}
void MultiSelectComboBox::setSearchBarPlaceHolderText(const QString & _text)
{
if(m_plineSer == nullptr)
{
return;
}
m_plineSer->setPlaceholderText(_text);
}
void MultiSelectComboBox::setPlaceHolderText(const QString & _text)
{
if(m_plineEdt == nullptr)
{
return;
}
m_plineEdt->setPlaceholderText(_text);
}
void MultiSelectComboBox::resetSelection()
{
if(m_plistWgt == nullptr || m_plineEdt == nullptr)
{
return;
}
m_plineEdt->clear();
int count = m_plistWgt->count();
for (int i = m_iCheckBoxStartIndex; i < count; i++)
{
QListWidgetItem * itemwgt = m_plistWgt->item(i);
if(itemwgt)
{
//设置用户数据为空
itemwgt->setData(Qt::UserRole,QVariant());
QWidget *widget = m_plistWgt->itemWidget(itemwgt);
if(widget)
{
QCheckBox *check_box = static_cast<QCheckBox*>(widget);
if(check_box)
{
check_box->setChecked(false);
}
}
}
}
}
void MultiSelectComboBox::clearCheckBox()
{
if(m_plineEdt == nullptr || m_plistWgt == nullptr)
{
return;
}
//清空文本显示框内容
m_plineEdt->clear();
m_vecSelItem.clear();
//清空下拉框中的checkbox控件
int count = m_plistWgt->count();
while (count != m_iCheckBoxStartIndex)
{
for (int i = m_iCheckBoxStartIndex; i < count; i++)
{
QListWidgetItem * itemwgt = m_plistWgt->item(i);
QWidget * widget = m_plistWgt->itemWidget(m_plistWgt->item(i));
if(widget)
{
QCheckBox * check_box = static_cast<QCheckBox*>(widget);
disconnect(check_box, &QCheckBox::stateChanged, this, &MultiSelectComboBox::stateChange);
delete check_box;
check_box = nullptr;
}
if(itemwgt)
{
m_plistWgt->removeItemWidget(itemwgt);
delete itemwgt;
itemwgt = nullptr;
}
}
count = m_plistWgt->count();
}
m_iCheckBoxNextIndex = 0;
m_iCheckBoxStartIndex = 0;
if(m_plineSer)
{
m_iCheckBoxNextIndex++;
m_iCheckBoxStartIndex++;
}
}
bool MultiSelectComboBox::eventFilter(QObject * watched, QEvent * event)
{
QFocusEvent * pfocusevent = (QFocusEvent *)event;
if(watched == this && pfocusevent->reason() == Qt::PopupFocusReason)
{//控件抓取/释放键盘焦点。
if(event->type() == QEvent::FocusAboutToChange)
{//打开下拉框
showPopup();
}
else if(event->type() == QEvent::FocusIn)
{//关闭下拉框
hidePopup();
}
return true;
}
else if(event->type() == QEvent::ContextMenu)
{//弹出菜单的事件不予理会
return true;
}
else if(((QMouseEvent *)event)->button() == Qt::RightButton)
{//弹出菜单的事件不予理会
return true;
}
return QComboBox::eventFilter(watched, event);
}
void MultiSelectComboBox::wheelEvent(QWheelEvent *event)
{
//禁用QComboBox默认的滚轮事件
Q_UNUSED(event);
}
void MultiSelectComboBox::keyPressEvent(QKeyEvent *event)
{
QComboBox::keyPressEvent(event);
}
void MultiSelectComboBox::stateChange(int state)
{
//获取信号的发送者
QObject* obj = sender();
QCheckBox * psender = dynamic_cast<QCheckBox*>(obj);
if(psender == nullptr || m_plistWgt == nullptr || m_plineEdt == nullptr)
{
return;
}
//获取用户数据
int count = m_plistWgt->count();
for (int i = m_iCheckBoxStartIndex; i < count; i++)
{
QWidget *widget = m_plistWgt->itemWidget(m_plistWgt->item(i));
QVariant qvardata = m_plistWgt->item(i)->data(Qt::UserRole + i);
STRUCT_CK_USER_DATA userdata = qvardata.value<STRUCT_CK_USER_DATA>();
QCheckBox *check_box = static_cast<QCheckBox*>(widget);
if (check_box && (psender == check_box))
{
if(state == Qt::Checked)
{
Q_EMIT SigSelChange(SELECT_TYPE_SEL,userdata.m_userdata);
}
else if(state == Qt::Unchecked)
{
Q_EMIT SigSelChange(SELECT_TYPE_REMOVESEL,userdata.m_userdata);
}
break;
}
}
//根据选择操作,增加或删除checkbox对象
if(psender)
{
if(state == Qt::Checked)
{
m_vecSelItem.push_back(psender);
}
else if(state == Qt::Unchecked)
{
for(int i = 0; i < m_vecSelItem.size(); ++i)
{
if(m_vecSelItem[i] && (m_vecSelItem[i] == psender))
{
m_vecSelItem.remove(i); //删除对应的checkbox对象
}
}
}
}
//每次都清空一下文本框
m_plineEdt->clear();
//重新显示文本框内容
QString qstr;
for(int i = 0; i < m_vecSelItem.size(); ++i)
{
qstr += m_vecSelItem[i]->text() + ";";
}
m_plineEdt->setText(qstr);
m_plineEdt->setToolTip(m_plineEdt->text());
}
void MultiSelectComboBox::onSearch(const QString& _text)
{
if(m_plistWgt == nullptr)
{
return;
}
for (int i = 1; i < m_plistWgt->count(); i++)
{
QCheckBox *check_box = static_cast<QCheckBox*>(m_plistWgt->itemWidget(m_plistWgt->item(i)));
//文本匹配则显示,反之隐藏
//Qt::CaseInsensitive模糊查询
if (check_box->text().contains(_text, Qt::CaseInsensitive)) {
m_plistWgt->item(i)->setHidden(false);
}
else
{
m_plistWgt->item(i)->setHidden(true);
}
}
}
void MultiSelectComboBox::itemClicked(int _index)
{
if(m_plistWgt == nullptr)
{
return;
}
if (_index != 0)
{
QCheckBox *check_box = static_cast<QCheckBox*>(m_plistWgt->itemWidget(m_plistWgt->item(_index)));
check_box->setChecked(!check_box->isChecked());
}
}
bool MultiSelectComboBox::CanHide()
{
if(m_plistWgt == nullptr)
{
return false;
}
if(m_plistWgt)
{
QPoint topleftpos = this->mapToGlobal(m_plistWgt->geometry().topLeft());
int topleftx = topleftpos.x();
int toplefty = topleftpos.y() + m_plineEdt->height();
int bottomrightx = topleftx + m_plistWgt->width();
int bottomrighty = toplefty + m_plistWgt->height();
QPoint mousepos = QCursor::pos();
if(mousepos.x() < topleftx || mousepos.x() > bottomrightx || mousepos.y() < toplefty || mousepos.y() > bottomrighty)
{//鼠标在下拉框界面外面
return true;
}
}
return false;
}
改进功能
- 增加用户数据,下拉框中的项可以增加用户数据。
- combobox的文本显示框会按照选择顺序显示选择项文本。
- 增加一些安全判断、优化注释。
- 增加样式表,但是并不是所有控件都增加了样式表。
2022-7-9修改记录
- 修改了清空下拉框元素后,再次添加崩溃的问题
- 修改了下拉框弹出的操作,只能通过点击下拉框箭头弹出,点击文本编辑框不能弹出下拉框