Qt之QComboBox下拉框实现勾选

QComboBox下拉界面带勾选


1、前言

QComboBox每次只能选择一个,现项目中需要可以多次勾选,不想基于第三方库qxtlib进行开发,所以自己参考qxtcheckcombobox源码,在此基础上进行修改。可保持QComboBox风格,不另行设置界面。


2、实现效果如下

在这里插入图片描述


3、思路

主体思路就是对于下拉的数据源model的数据标志位添加可勾选属性。
下面列举一些Qt::ItemDataRole

ConstantValueDescription
Qt::FontRole6The font used for items rendered with the default delegate. (QFont)
Qt::TextAlignmentRole7The alignment of the text for items rendered with the default delegate. (Qt::Alignment)
Qt::BackgroundRole8The background brush used for items rendered with the default delegate. (QBrush)
Qt::BackgroundColorRole8This role is obsolete. Use BackgroundRole instead.
Qt::ForegroundRole9The foreground brush (text color, typically) used for items rendered with the default delegate. (QBrush)
Qt::TextColorRole9This role is obsolete. Use ForegroundRole instead.
Qt::CheckStateRole10This role is used to obtain the checked state of an item. (Qt::CheckState)
Qt::InitialSortOrderRole14This role is used to obtain the initial sort order of a header view section. (Qt::SortOrder). This role was introduced in Qt 4.8.

4、代码干货

以下代码仅供参考,可根据实际需求修改

class DSCheckComboModel : public QStandardItemModel
{
	Q_OBJECT

public:
	explicit DSCheckComboModel(QObject* parent = Q_NULLPTR);

	virtual Qt::ItemFlags flags(const QModelIndex& index) const;
	virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
	virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);

Q_SIGNALS:
	void checkStateChanged();
};

class DSCheckComboBoxPrivate;
class DSCheckComboBox : public QComboBox
{
	Q_OBJECT

	Q_PROPERTY(QString separator READ separator WRITE setSeparator)
	Q_PROPERTY(QString defaultText READ defaultText WRITE setDefaultText)
	Q_PROPERTY(QStringList checkedItems READ checkedItems WRITE setCheckedItems)

public:
	DSCheckComboBox(QWidget *parent = Q_NULLPTR);
	~DSCheckComboBox();

	//隐藏下拉框
	virtual void hidePopup();

	//获取默认文本
	QString defaultText() const;
	//设置默认文本
	void setDefaultText(const QString& text);

	//获取勾选状态
	Qt::CheckState itemCheckState(int index) const;
	//设置勾选状态
	void setItemCheckState(int index, Qt::CheckState state);

	//获取字符串分割方式
	QString separator() const;
	//设置字符串分割方式
	void setSeparator(const QString& separator);

	//获取所有勾选的文本
	QStringList checkedItems() const;
	//获取所有勾线的下标
	QVariantList checkedIndexs() const;

public Q_SLOTS:
	void setCheckedItems(const QStringList& items);

Q_SIGNALS:
	void checkedItemsChanged(const QStringList& items);

protected:
	virtual bool eventFilter(QObject *watched, QEvent *event);
	virtual void wheelEvent(QWheelEvent *e);

private:
	QScopedPointer<DSCheckComboBoxPrivate> d_ptr;
	Q_DISABLE_COPY(DSCheckComboBox)
	Q_DECLARE_PRIVATE(DSCheckComboBox)
	Q_PRIVATE_SLOT(d_func(), void updateCheckedItems())
	Q_PRIVATE_SLOT(d_func(), void toggleCheckState(int index))
};

DSCheckComboModel::DSCheckComboModel(QObject* parent /* = Q_NULLPTR */)
	: QStandardItemModel(0, 1, parent) //rows,cols
{
}

Qt::ItemFlags DSCheckComboModel::flags(const QModelIndex& index) const
{
	return QStandardItemModel::flags(index) | Qt::ItemIsUserCheckable;
}

QVariant DSCheckComboModel::data(const QModelIndex& index, int role /* = Qt::DisplayRole */) const
{
	QVariant value = QStandardItemModel::data(index, role);
	if (index.isValid() && role == Qt::CheckStateRole && !value.isValid())
		value = Qt::Unchecked;

	return value;
}

bool DSCheckComboModel::setData(const QModelIndex& index, const QVariant& value, int role /* = Qt::EditRole */)
{
	bool ok = QStandardItemModel::setData(index, value, role);
	if (ok && role == Qt::CheckStateRole)
	{
		emit dataChanged(index, index);
		emit checkStateChanged();
	}

	return ok;
}


class DSCheckComboBoxPrivate : public QObject
{
	Q_DECLARE_PUBLIC(DSCheckComboBox)
	DSCheckComboBox* q_ptr;

public:
	DSCheckComboBoxPrivate(QObject* parent = Q_NULLPTR);
	bool eventFilter(QObject* obj, QEvent* e);

	QString separator;
	QString defaultText;
	bool containerMousePress;

	void updateCheckedItems();
	void toggleCheckState(int index);
};

DSCheckComboBoxPrivate::DSCheckComboBoxPrivate(QObject* parent)
	: containerMousePress(false)
{
	separator = QLatin1String(",");
}

bool DSCheckComboBoxPrivate::eventFilter(QObject* obj, QEvent* e)
{
	switch (e->type())
	{
	case QEvent::KeyPress:
	case QEvent::KeyRelease:
	{
		QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
		if (obj == q_ptr && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down))
		{
			q_ptr->showPopup();
			return true;
		}
		else if (keyEvent->key() == Qt::Key_Enter || 
				 keyEvent->key() == Qt::Key_Return || 
				 keyEvent->key() == Qt::Key_Escape)
		{
			q_ptr->hidePopup();
			if (keyEvent->type() != Qt::Key_Escape)
				return true;
		}
	}
	case QEvent::MouseButtonPress:
		containerMousePress = (obj == q_ptr->view()->window());
		break;
	case QEvent::MouseButtonRelease:
		containerMousePress = false;
		break;
	default:
		break;
	}
	return false;
}

void DSCheckComboBoxPrivate::updateCheckedItems()
{
	QStringList items = q_ptr->checkedItems();
	if (items.empty())
		q_ptr->setEditText(defaultText);
	else
		q_ptr->setEditText(items.join(separator));

	emit q_ptr->checkedItemsChanged(items);
}

void DSCheckComboBoxPrivate::toggleCheckState(int index)
{
	QVariant value = q_ptr->itemData(index, Qt::CheckStateRole);
	if (value.isValid())
	{
		Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
		q_ptr->setItemData(index, (state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole);
	}
}

DSCheckComboBox::DSCheckComboBox(QWidget *parent)
	: QComboBox(parent)
	, d_ptr(new DSCheckComboBoxPrivate)
{
	d_ptr->q_ptr = this;
	setModel(new DSCheckComboModel(this));

	connect(this, SIGNAL(activated(int)), this, SLOT(toggleCheckState(int)));
	connect(model(), SIGNAL(checkStateChanged()), this, SLOT(updateCheckedItems()));
	connect(model(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), this, SLOT(updateCheckedItems()));
	connect(model(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(updateCheckedItems()));

	QLineEdit* lineEdit = new QLineEdit(this);
	lineEdit->setReadOnly(true);
	setLineEdit(lineEdit);
	lineEdit->disconnect(this);
	setInsertPolicy(QComboBox::NoInsert);

	installEventFilter(this);
	view()->installEventFilter(this);
	view()->window()->installEventFilter(this);
	view()->viewport()->installEventFilter(this);
}

DSCheckComboBox::~DSCheckComboBox()
{
}

void DSCheckComboBox::hidePopup()
{
	if (d_ptr->containerMousePress)
		QComboBox::hidePopup();
}

Qt::CheckState DSCheckComboBox::itemCheckState(int index) const
{
	return static_cast<Qt::CheckState>(itemData(index, Qt::CheckStateRole).toInt());
}

void DSCheckComboBox::setItemCheckState(int index, Qt::CheckState state)
{
	setItemData(index, state, Qt::CheckStateRole);
}

QStringList DSCheckComboBox::checkedItems() const
{
	QStringList items;
	if (model())
	{
		QModelIndex index = model()->index(0, modelColumn(), rootModelIndex());
		QModelIndexList indexs = model()->match(index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly);
		foreach(const QModelIndex& index, indexs)
			items += index.data().toString();
	}
	return items;
}

QVariantList DSCheckComboBox::checkedIndexs() const
{
	QVariantList indexs;
	if (model())
	{
		QModelIndex index = model()->index(0, modelColumn(), rootModelIndex());
		QModelIndexList indexs_2 = model()->match(index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly);
		foreach(const QModelIndex& index, indexs_2)
			indexs += index.row();
	}
	return indexs;
}

void DSCheckComboBox::setCheckedItems(const QStringList& items)
{
	foreach(const QString& text, items)
	{
		const int index = findText(text);
		setItemCheckState(index, index != -1 ? Qt::Checked : Qt::Unchecked);
	}
}

bool DSCheckComboBox::eventFilter(QObject *watched, QEvent *event)
{
	return d_ptr->eventFilter(watched,event);
}

void DSCheckComboBox::wheelEvent(QWheelEvent *e)
{
	e->accept();
}

QString DSCheckComboBox::defaultText() const
{
	return d_ptr->defaultText;
}

void DSCheckComboBox::setDefaultText(const QString& text)
{
	if (d_ptr->defaultText != text)
	{
		d_ptr->defaultText = text;
		d_ptr->updateCheckedItems();
	}
}

QString DSCheckComboBox::separator() const
{
	return d_ptr->separator;
}

void DSCheckComboBox::setSeparator(const QString& separator)
{
	if (d_ptr->separator != separator)
	{
		d_ptr->separator = separator;
		d_ptr->updateCheckedItems();
	}
}

以上代码大家可只做参考。

源码免费在此传送门


5、注意事项

注意: 因为cpp中定义的Private类包含Q_OBJECT,所以需要在源文件中包含 #include “moc_DSCheckComboBox.cpp”,如果工程能正常编译则不需要要进行后续操作
后续操作:修改头文件编译条件为如下:在这里插入图片描述

在常规中修改如下:
在这里插入图片描述

在commanderline中输入命令:"$(QTDIR)/bin/moc.exe" "%(FullPath)" -o "./GeneratedFiles/$(Configuration)/moc_%(Filename).cpp" -D -DQT_CORE_LIB -DQT_GUI_LIB -DQT_LARGEFILE_SUPPORT -DQT_THREAD_SUPPORT -DUNICODE -DWIN32 -I"$(QTDIR)/include" -I"$(QTDIR)/include/QtCore" -I"$(QTDIR)/include/QtGui" -I"$(QTDIR)/include/qtmain" -I"." -I"./GeneratedFiles" -I"./GeneratedFiles/$(Configuration)"

Description中输入:Moc%27ing %(Filename)%(Extension)...

OutPuts中输入:./GeneratedFiles/$(Configuration)/moc_%(Filename).cpp

AdditionnalOutPuts中输入:$(QTDIR)/bin/moc.exe;%(FullPath);%(AdditionalInputs)

具体为什么这里就不过多说明,可度娘。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值