Qt提供的QComboBox只能选择其中一个选项,无法实现同时选中多个,而实际工程项目中,下拉框中带复选框的需求比比皆是。阅读了网上大量的博客,但是没有发现一个能完美的实现该功能的ComboBox,都存在各种未解决的bug缺陷,样子是那么回事儿,但是里子经不起推敲。经过笔者多次熬夜,接近通宵,工程中反复使用、测试,bug逐渐被解决。
废话不多说,下面就教大家如何实现。
1、初始化样式
2、展开样式
3、选择多个item的样式
4、选择后的样式,鼠标hover时,显示被选中的item文本
1、头文件XComboBox.h
#pragma once
#include
#include
#include
#include
class QLineEdit;
class QListView;
struct ItemInfo
{
int idx;
QString str;
QVariant userData;
bool bChecked;
ItemInfo()
{
idx = -1;
str = QString("");
userData = QVariant();
bChecked = false;
}
};
// 事件过滤器
class KeyPressEater : public QObject
{
Q_OBJECT
public:
KeyPressEater(QObject* parent=nullptr):QObject(parent) {}
~KeyPressEater() {}
signals:
void sigActivated(int idx);
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast(event);
if (keyEvent->key() == Qt::Key_Space)
{
QListView* lstV = qobject_cast(obj);
if (nullptr != lstV)
{
int idx = lstV->currentIndex().row();
if (-1 != idx)
{
emit sigActivated(idx);
}
}
}
else if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)
{
return QObject::eventFilter(obj, event);
}
return true;
}
else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
};
class XComboBox : public QComboBox
{
Q_OBJECT
public:
XComboBox(QWidget *parent = Q_NULLPTR);
~XComboBox();
// 添加item
void AddItem(const QString& str, bool bChecked = false, QVariant &userData = QVariant());
void AddItems(const QList& lstItemInfo);
void AddItems(const QMap& mapStrChk);
void AddItems(const QList& lstStr);
// 删除item
void RemoveItem(int idx);
// 清空item
void Clear();
// 获取选中的数据字符串列表
QStringList GetSelItemsText();
// 获取选中item的信息
QList GetSelItemsInfo();
// 获取item文本
QString GetItemText(int idx);
// 获取item信息
ItemInfo GetItemInfo(int idx);
signals:
// popup显示信号
void showingPopup();
// popup隐藏信号
void hidingPopup();
protected:
void showPopup();
// 重写QComboBox的hidePopup函数
// 目的选择过程中,不隐藏listview
void hidePopup();
virtual void mousePressEvent(QMouseEvent * event);
virtual void mouseReleaseEvent(QMouseEvent * event);
virtual void mouseMoveEvent(QMouseEvent * event);
private:
void UpdateText();
private slots:
void sltActivated(int idx);
private:
QLineEdit* pLineEdit;
QListView* pListView;
QStandardItemModel m_model;
};
2、源文件XComboBox.cpp
#include "XComboBox.h"
#include
#include
#include
XComboBox::XComboBox(QWidget *parent)
: QComboBox(parent)
{
pLineEdit = new QLineEdit(this);
pLineEdit->setReadOnly(true);
this->setLineEdit(pLineEdit);
this->lineEdit()->disconnect();
KeyPressEater *keyPressEater = new KeyPressEater(this);
pListView = new QListView(this);
pListView->installEventFilter(keyPressEater);
this->setView(pListView);
this->setModel(&m_model);
connect(this, SIGNAL(activated(int)), this, SLOT(sltActivated(int)));
connect(keyPressEater, SIGNAL(sigActivated(int)), this, SLOT(sltActivated(int)));
}
XComboBox::~XComboBox()
{
}
void XComboBox::AddItem(const QString& str, bool bChecked /*= false*/, QVariant &userData /*= QVariant()*/)
{
QStandardItem* item = new QStandardItem(str);
item->setCheckable(true);
item->setCheckState(bChecked ? Qt::Checked : Qt::Unchecked);
item->setData(userData, Qt::UserRole + 1);
m_model.appendRow(item);
UpdateText();
}
void XComboBox::AddItems(const QList& lstItemInfo)
{
for (auto a : lstItemInfo)
{
AddItem(a.str, a.bChecked, a.userData);
}
}
void XComboBox::AddItems(const QMap& mapStrChk)
{
for (auto it = mapStrChk.begin(); it != mapStrChk.end(); ++it)
{
AddItem(it.key(), it.value());
}
}
void XComboBox::AddItems(const QList& lstStr)
{
for (auto a : lstStr)
{
AddItem(a, false);
}
}
void XComboBox::RemoveItem(int idx)
{
m_model.removeRow(idx);
UpdateText();
}
void XComboBox::Clear()
{
m_model.clear();
UpdateText();
}
QStringList XComboBox::GetSelItemsText()
{
QStringList lst;
QString str = pLineEdit->text();
if (str.isEmpty())
{
return lst;
}
else
{
return pLineEdit->text().split(",");
}
}
QList XComboBox::GetSelItemsInfo()
{
QList lstInfo;
for (int i = 0; i < m_model.rowCount(); i++)
{
QStandardItem* item = m_model.item(i);
if (item->checkState() == Qt::Unchecked) continue;
ItemInfo info;
info.idx = i;
info.str = item->text();
info.bChecked = true;
info.userData = item->data(Qt::UserRole + 1);
lstInfo << info;
}
return lstInfo;
}
QString XComboBox::GetItemText(int idx)
{
if (idx < 0 || idx >= m_model.rowCount())
{
return QString("");
}
return m_model.item(idx)->text();
}
ItemInfo XComboBox::GetItemInfo(int idx)
{
ItemInfo info;
if (idx < 0 || idx >= m_model.rowCount())
{
return info;
}
QStandardItem* item = m_model.item(idx);
info.idx = idx;
info.str = item->text();
info.bChecked = (item->checkState() == Qt::Checked);
info.userData = item->data(Qt::UserRole + 1);
return info;
}
void XComboBox::showPopup()
{
emit showingPopup();
QComboBox::showPopup();
}
void XComboBox::hidePopup()
{
int width = this->view()->width();
int height = this->view()->height();
int x = QCursor::pos().x() - mapToGlobal(geometry().topLeft()).x() + geometry().x();
int y = QCursor::pos().y() - mapToGlobal(geometry().topLeft()).y() + geometry().y();
QRect rectView(0, this->height(), width, height);
if (!rectView.contains(x, y))
{
emit hidingPopup();
QComboBox::hidePopup();
}
}
void XComboBox::mousePressEvent(QMouseEvent * event)
{
QComboBox::mousePressEvent(event);
event->accept();
}
void XComboBox::mouseReleaseEvent(QMouseEvent * event)
{
QComboBox::mouseReleaseEvent(event);
event->accept();
}
void XComboBox::mouseMoveEvent(QMouseEvent * event)
{
QComboBox::mouseMoveEvent(event);
event->accept();
}
void XComboBox::UpdateText()
{
QStringList lstTxt;
for (int i = 0; i < m_model.rowCount(); ++i)
{
QStandardItem* item = m_model.item(i);
if (item->checkState() == Qt::Unchecked) continue;
lstTxt << item->text();
}
pLineEdit->setText(lstTxt.join(","));
pLineEdit->setToolTip(lstTxt.join("\n"));
}
void XComboBox::sltActivated(int idx)
{
QStandardItem* item = m_model.item(idx);
if (nullptr == item) return;
Qt::CheckState state = (item->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
item->setCheckState(state);
UpdateText();
}
3、测试代码
QStringList lstStr;
for (int i=0; i<10; ++i)
{
lstStr << QString("item %1").arg(i);
}
ui.cmb->AddItems(lstStr);
三、重难点
如何避免原生QComboBox内部信号对自定的XComboBox的影响
四、源码下载链接如下
https://download.csdn.net/download/u013001137/11634199