一、效果图

十五、带复选框的ComboBox(自定义QComboBox)_ide

二、代码实现

MenuItem.h

#ifndef MENUITEM_H
#define MENUITEM_H

#include <QObject>

class MenuItem
{
public:
MenuItem(int menuId, const QString &menuName, bool isChecked = false);
MenuItem();
//拷贝构造函数,载遍历类集合时,需要使用
MenuItem(const MenuItem &menuItem);
int getMenuId() const;
void setMenuId(int value);

QString getMenuName() const;
void setMenuName(const QString &value);

bool getIsChecked() const;
void setIsChecked(bool value);

private:
int menuId;
QString menuName;
bool isChecked;
};

#endif // MENUITEM_H

MenuItem.cpp

#include "menuitem.h"

MenuItem::MenuItem(int menuId, const QString &menuName, bool isChecked)
{
this->menuId = menuId;
this->menuName = menuName;
this->isChecked = isChecked;
}

MenuItem::MenuItem()
{
this->menuId = -1;
this->menuName = QString("");
this->isChecked = false;
}

MenuItem::MenuItem(const MenuItem &menuItem)
{
this->menuId = menuItem.getMenuId();
this->menuName = menuItem.getMenuName();
this->isChecked = menuItem.getIsChecked();
}

int MenuItem::getMenuId() const
{
return menuId;
}

void MenuItem::setMenuId(int value)
{
menuId = value;
}

QString MenuItem::getMenuName() const
{
return menuName;
}

void MenuItem::setMenuName(const QString &value)
{
menuName = value;
}

bool MenuItem::getIsChecked() const
{
return isChecked;
}

void MenuItem::setIsChecked(bool value)
{
isChecked = value;
}

MultiComBox.h

#ifndef MULTICOMBOX_H
#define MULTICOMBOX_H

#include <QWidget>
#include <QComboBox>

class MenuItem;
class QLineEdit;
class QListView;
class QStandardItemModel;

class MultiComBox : public QComboBox
{
Q_OBJECT
public:
MultiComBox(QWidget *parent = Q_NULLPTR);
~MultiComBox();

//添加 item
void addItems(QList<MenuItem*> menus);
/**
* @brief 移除 Item
* @param row 行数
*/
void removeItem(int row);

void clear();
//获取选中的字符串
QStringList getItemsText();
//获取选中项的 id 集合
QList<int> getItemIds();

signals:
//选中 item 时,发送信号
void sigActivated(int row);

protected:
void hidePopup() Q_DECL_OVERRIDE;
bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;

private:
QList<MenuItem> menus;
QLineEdit *pLineEdit;
QListView *pListView;
QStandardItemModel *itemModel;

private:
//更新选中字符串
void updateText();

public slots:
//响应单击 item
void sltActivated(int row);
};

#endif // MULTICOMBOX_H

MultiComBox.cpp

#include "multicombox.h"

#include "menuitem.h"
#include "../app.h"

#include <QLineEdit>
#include <QListView>
#include <QStandardItemModel>
#include <QMouseEvent>

MultiComBox::MultiComBox(QWidget *parent) : QComboBox(parent)
{
//为 ComBox 设置编辑框
pLineEdit = new QLineEdit(this);
pLineEdit->setReadOnly(true);
this->setLineEdit(pLineEdit);

//设置 ComBox 下拉界面
pListView = new QListView(this);
pListView->installEventFilter(this);
this->setView(pListView);

//设置 ComBox 数据模型
itemModel = new QStandardItemModel(this);
this->setModel(itemModel);

connect(this, SIGNAL(activated(int)), this, SLOT(sltActivated(int)));
}

MultiComBox::~MultiComBox()
{

}

void MultiComBox::addItems(QList<MenuItem*> menus)
{
QStandardItem *newItem;
for (MenuItem *menuItem : menus) {
newItem = new QStandardItem(menuItem->getMenuName());
//不加这一段代码,不出现单选框
newItem->setCheckable(Qt::Checked);
newItem->setCheckable(menuItem->getIsChecked() ? Qt::Checked : Qt::Unchecked);
newItem->setData(menuItem->getMenuId());
itemModel->appendRow(newItem);
}
updateText();
}

void MultiComBox::removeItem(int row)
{
itemModel->removeRow(row);
updateText();
}

void MultiComBox::clear()
{
itemModel->clear();
updateText();
}

QStringList MultiComBox::getItemsText()
{
QStringList strs;
QString str = pLineEdit->text();
if (!str.isEmpty()) {
strs = str.split(App::SEPERATOR);
}
return strs;
}

QList<int> MultiComBox::getItemIds()
{
QList<int> ids;
for (int i=0; i<itemModel->rowCount(); i++) {
QStandardItem *item = itemModel->item(i);
if (item->checkState() == Qt::Checked) {
ids << item->data().toInt();
}
}
return ids;
}

/**
* 目标:通过判断鼠标是否还在 ComBox 下拉框区域,来判断,是否隐藏下拉框
*/
void MultiComBox::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)) {
QComboBox::hidePopup();
}
}

bool MultiComBox::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::LeftButton) {
QListView *listView = qobject_cast<QListView*>(watched);
if (nullptr != listView) {
int row = listView->currentIndex().row();
if (row != -1) {
sltActivated(row);
}
}
return true;
}
}
return QComboBox::eventFilter(watched, event);
}

void MultiComBox::updateText()
{
QStringList strs;
for (int i=0; i<itemModel->rowCount(); i++) {
QStandardItem *item = itemModel->item(i);
if (item->checkState() == Qt::Checked) {
strs << item->text();
}
}
pLineEdit->setText(strs.join(App::SEPERATOR));
pLineEdit->setToolTip(strs.join("\n"));
}

void MultiComBox::sltActivated(int row)
{
QStandardItem *item = itemModel->item(row);
Qt::CheckState checkState = item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked;
item->setCheckState(checkState);
updateText();
}