目录
1.整体功能解析
实现效果如下:

带复选框多选功能的掩码组合框 QMaskComboBox ,继承自 QComboBox,核心特性:
- 用
uint64_t位掩码管理选项,支持多选 - 下拉列表用
QListWidget+QCheckBox实现 - 文本框显示选中项拼接结果,工具提示显示完整选中项
- 提供信号
selectMaskPopup,在下拉框隐藏时通知当前掩码值
2.核心模块拆解
2.1.头文件结构(QMaskComboBox.h)
cpp
class QMaskComboBox : public QComboBox
{
Q_OBJECT
public:
using mask_vector = QVector<std::pair<uint64_t, QString>>;
explicit QMaskComboBox(QWidget* parent = nullptr);
explicit QMaskComboBox(const mask_vector& maskList, uint64_t val = 0, QWidget* parent = nullptr);
const mask_vector& mask() const;
void setMask(const mask_vector& val);
void setMask(const mask_vector& val, uint64_t select);
uint64_t selectMask() const noexcept;
void setSelectMask(uint64_t val);
void hidePopup() override;
bool eventFilter(QObject* watched, QEvent* evt) override;
Q_SIGNALS:
void selectMaskPopup(qulonglong);
private:
void refresh();
private slots:
void onStateChanged(uint64_t bits, int state);
private:
QListWidget* m_pListWidget;
mask_vector m_mask;
uint64_t m_selectMask;
};
mask_vector:存储 "掩码值 - 显示文本" 的映射m_selectMask:当前选中的位掩码refresh():核心刷新函数,重建下拉列表onStateChanged:复选框状态变化的槽函数,更新掩码和文本
2.2.关键实现解析
1.构造函数与事件过滤
cpp
QMaskComboBox::QMaskComboBox(QWidget* parent)
: QComboBox(parent)
, m_pListWidget(nullptr)
, m_selectMask(0)
{
setEditable(true);
lineEdit()->installEventFilter(this);
}
bool QMaskComboBox::eventFilter(QObject* watched, QEvent* evt)
{
if (watched == lineEdit() && evt->type() == QEvent::MouseButtonPress)
{
showPopup();
return true;
}
return QComboBox::eventFilter(watched, evt);
}
- 开启
setEditable(true),并设置lineEdit为只读(在refresh()中),让用户无法直接编辑文本 - 事件过滤器:点击文本框时直接弹出下拉框,和原生
QComboBox行为对齐
2.refresh() 函数(核心逻辑)
cpp
void QMaskComboBox::refresh()
{
clear();
delete model();
delete view();
lineEdit()->setReadOnly(true);
m_pListWidget = new QListWidget(this);
m_pListWidget->setObjectName("listView");
QString selectData;
QString tipData;
for (auto& kv : m_mask)
{
uint64_t bits = kv.first;
QString desc = kv.second;
QListWidgetItem* pItem = new QListWidgetItem(m_pListWidget);
pItem->setData(Qt::UserRole, QVariant::fromValue(bits));
QCheckBox* pCheckBox = new QCheckBox(this);
pCheckBox->setText(desc);
bool checked = (bits & m_selectMask) == bits;
pCheckBox->setChecked(checked);
if (checked)
{
if (!selectData.isEmpty())
{
selectData.append(";");
tipData.append("\n");
}
selectData.append(kv.second);
tipData.append(kv.second);
}
connect(pCheckBox, &QCheckBox::stateChanged,
[this, bits](int state) { onStateChanged(bits, state); });
m_pListWidget->addItem(pItem);
m_pListWidget->setItemWidget(pItem, pCheckBox);
}
setModel(m_pListWidget->model());
setView(m_pListWidget);
if (!selectData.isEmpty())
{
lineEdit()->setText(selectData);
lineEdit()->setToolTip(tipData);
}
else
{
lineEdit()->clear();
}
}
- 每次调用
refresh()都会重建下拉列表:删除旧的model和view,创建新的QListWidget - 遍历
m_mask,为每个选项创建QListWidgetItem和QCheckBox - 根据
m_selectMask初始化复选框状态,并拼接选中项文本 - 连接复选框的
stateChanged信号到onStateChanged槽
3.onStateChanged 槽函数
cpp
void QMaskComboBox::onStateChanged(uint64_t bits, int state)
{
if (state)
m_selectMask |= bits;
else
m_selectMask &= ~bits;
QString selectData;
QString tipData;
int nCount = m_pListWidget->count();
for (int i = 0; i < nCount; ++i)
{
QListWidgetItem* pItem = m_pListWidget->item(i);
QWidget* pWidget = m_pListWidget->itemWidget(pItem);
QCheckBox* pCheckBox = qobject_cast<QCheckBox*>(pWidget);
const auto& kv = m_mask[i];
bool checked = (kv.first & m_selectMask) == kv.first;
pCheckBox->setChecked(checked);
if (checked)
{
if (!selectData.isEmpty())
{
selectData.append(";");
tipData.append("\n");
}
selectData.append(kv.second);
tipData.append(kv.second);
}
}
if (!selectData.isEmpty())
{
lineEdit()->setText(selectData);
lineEdit()->setToolTip(tipData);
}
else
{
lineEdit()->clear();
}
}
- 根据复选框状态更新
m_selectMask:选中则按位或,取消则按位与非 - 遍历所有复选框,同步状态并更新文本框显示
4.hidePopup() 重写
cpp
void QMaskComboBox::hidePopup()
{
emit selectMaskPopup(m_selectMask);
QComboBox::hidePopup();
}
下拉框隐藏时,发射 selectMaskPopup 信号,通知外部当前掩码值
3.完整代码示例
cpp
// QMaskComboBox.h
#ifndef QMASKCOMBOBOX_H
#define QMASKCOMBOBOX_H
#include <QComboBox>
#include <QListWidget>
#include <QCheckBox>
class QMaskComboBox : public QComboBox
{
Q_OBJECT
public:
using mask_vector = QVector<std::pair<uint64_t, QString>>;
explicit QMaskComboBox(QWidget* parent = nullptr);
explicit QMaskComboBox(const mask_vector& maskList, uint64_t val = 0, QWidget* parent = nullptr);
const mask_vector& mask() const;
void setMask(const mask_vector& val);
void setMask(const mask_vector& val, uint64_t select);
uint64_t selectMask() const noexcept;
void setSelectMask(uint64_t val);
void hidePopup() override;
bool eventFilter(QObject* watched, QEvent* evt) override;
Q_SIGNALS:
void selectMaskPopup(qulonglong);
private:
void refresh();
void updateText();
private slots:
void onStateChanged(int state);
private:
mask_vector m_mask;
uint64_t m_selectMask = 0;
QListWidget* m_pListWidget = nullptr;
};
#endif // QMASKCOMBOBOX_H
cpp
// QMaskComboBox.cpp
#include "QMaskComboBox.h"
QMaskComboBox::QMaskComboBox(QWidget* parent)
: QComboBox(parent)
{
setEditable(true);
lineEdit()->setReadOnly(true);
lineEdit()->installEventFilter(this);
}
QMaskComboBox::QMaskComboBox(const mask_vector& maskList, uint64_t val, QWidget* parent)
: QMaskComboBox(parent)
{
m_mask = maskList;
m_selectMask = val;
refresh();
}
const QMaskComboBox::mask_vector& QMaskComboBox::mask() const
{
return m_mask;
}
void QMaskComboBox::setMask(const mask_vector& val)
{
m_mask = val;
refresh();
}
void QMaskComboBox::setMask(const mask_vector& val, uint64_t select)
{
m_mask = val;
m_selectMask = select;
refresh();
}
uint64_t QMaskComboBox::selectMask() const noexcept
{
return m_selectMask;
}
void QMaskComboBox::setSelectMask(uint64_t val)
{
m_selectMask = val;
updateText();
refresh();
}
bool QMaskComboBox::eventFilter(QObject* watched, QEvent* evt)
{
if (watched == lineEdit() && evt->type() == QEvent::MouseButtonPress)
{
showPopup();
return true;
}
return QComboBox::eventFilter(watched, evt);
}
void QMaskComboBox::hidePopup()
{
emit selectMaskPopup(m_selectMask);
QComboBox::hidePopup();
}
void QMaskComboBox::refresh()
{
// 清理旧的 view 和 model
if (m_pListWidget)
{
delete m_pListWidget;
m_pListWidget = nullptr;
}
clear();
setModel(nullptr);
setView(nullptr);
m_pListWidget = new QListWidget(this);
m_pListWidget->setObjectName("listView");
for (auto& kv : m_mask)
{
uint64_t bits = kv.first;
QString desc = kv.second;
QListWidgetItem* pItem = new QListWidgetItem(m_pListWidget);
QCheckBox* pCheckBox = new QCheckBox(desc, m_pListWidget);
pCheckBox->setProperty("maskBits", bits);
bool checked = (bits & m_selectMask) == bits;
pCheckBox->setChecked(checked);
connect(pCheckBox, &QCheckBox::stateChanged, this, &QMaskComboBox::onStateChanged);
m_pListWidget->setItemWidget(pItem, pCheckBox);
}
setModel(m_pListWidget->model());
setView(m_pListWidget);
updateText();
}
void QMaskComboBox::updateText()
{
QString selectData;
QString tipData;
for (auto& kv : m_mask)
{
if ((kv.first & m_selectMask) == kv.first)
{
if (!selectData.isEmpty())
{
selectData.append(";");
tipData.append("\n");
}
selectData.append(kv.second);
tipData.append(kv.second);
}
}
if (!selectData.isEmpty())
{
lineEdit()->setText(selectData);
lineEdit()->setToolTip(tipData);
}
else
{
lineEdit()->clear();
lineEdit()->setToolTip("");
}
}
void QMaskComboBox::onStateChanged(int state)
{
QCheckBox* pCheckBox = qobject_cast<QCheckBox*>(sender());
if (!pCheckBox) return;
uint64_t bits = pCheckBox->property("maskBits").toULongLong();
if (state)
m_selectMask |= bits;
else
m_selectMask &= ~bits;
updateText();
}
4.使用示例
cpp
// 定义掩码选项
QMaskComboBox::mask_vector masks = {
{0x01, "选项A"},
{0x02, "选项B"},
{0x04, "选项C"},
{0x08, "选项D"}
};
// 创建控件并设置选项
QMaskComboBox* combo = new QMaskComboBox(this);
combo->setMask(masks, 0x01 | 0x04); // 默认选中选项A和C
// 监听掩码变化
connect(combo, &QMaskComboBox::selectMaskPopup, this, [](qulonglong mask) {
qDebug() << "当前选中掩码:" << mask;
});