• Qt 下拉复选框(MultiSelectComboBox)(一) 实现下拉框多选,搜索下拉框内容


    文章目录


    前言

    刚开始学习qt时,在遇到下拉复选框问题的时候总是使用表格来实现这个功能,因为表格单元格可以添加代理,而QComboBox类对象在设置代理后总是不生效。使用表格来实现,如果需求本来就是在表格中还好,但有时候只是需要一个下拉复选框,这个时候就需要隐藏边框,设置单元双击输入框禁用等等,会非常麻烦,而且最后的效果也是不敬人意,所以在仔细研究了QComboBox类对象之后,参考github大佬的示例实现了一个比较好用的下拉复选框。

    在这里插入图片描述


    参考博客地址:https://blog.csdn.net/u013135921/article/details/79437392
    以下是本篇文章正文内容,主要实现功能,下面案例可供参考

    一、QCombobox的组成

    QComboBox类对象由三部分组成:QLineEdit、QToolButton、QListWidget。
    在这里插入图片描述

    • QLineEdit负责选中选项的显示,由此要设置为不可编辑状态。
    • QToolButton是一个弹出与隐藏下拉框的按钮。
    • QListWidget是最重要的一部分,所有选项内容显示的格式,自定义功能的添加主要都集中在这部分。

    二、MultiSelectComboBox实现

    1. 总体实现

    整个控件继承于QCombobox类。主要修改QLineEdit、QListWidget这两部分,QComboBox提供如下接口,可以将这两部分设置为新建的QLineEdit、QListWidget对象 ( line_edit_,list_widget_)。

    代码如下(示例):

    this->setView(list_widget_);
    this->setLineEdit(line_edit_);
    
    • 1
    • 2
    • line_edit_部分用来显示选择的结果和弹出下拉框,显示内容使用 “;” 进行分割
    • list_widget_下拉框部分,显示搜索框与选项。QListWidget类对象可以通过设置Item,在item中添加QWidget对象来实现一些特殊功能。这里的list_widget_,搜索框为QlineEdit类对象,用户输入搜索条件后显示符合条件的选项,用来解决选项过多的问题,选项为QCheckBox类对象,有“选中”和“未选中”两种状态。

    2. QLineEdit部分

    这部分用来显示选择的结果和弹出下拉框,显示内容使用 “;” 进行分割。这里设置了点击QLineEdit也可以弹出下拉框,需要先将当前line_edit_对象安装(或注册)为事件过滤器,再重写eventFilter()函数。

    代码如下(示例):

        /*设置文本框*/
    	//设为只读,因为该输入框只用来显示选中的选项,称为文本框更合适些
    	line_edit_->setReadOnly(true);
    	//把当前对象安装(或注册)为事件过滤器,当前也称为过滤器对象。事件过滤器通常在构造函数中进行注册。
    	line_edit_->installEventFilter(this);
    	//设置禁用样式,因为不受样式表控制,临时这样解决
    	line_edit_->setStyleSheet("QLineEdit:disabled{background:rgb(233,233,233);}");
    
    
    bool MultiSelectComboBox::eventFilter(QObject *watched, QEvent *event)
    {
    	//设置点击输入框也可以弹出下拉框
    	if (watched == line_edit_ && event->type() == QEvent::MouseButtonRelease && this->isEnabled())
    	{
    		showPopup();
    		return true;
    	}
    	return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. QListWidget部分

    下拉框部分,显示搜索框与选项。

    1. 搜索框部分

    设置list_widget_第一项的itemWidget为QLineEdit,用来输入检索条件,绑定textChanged(const QString&)信号,当输入内容发生变化时,下面显示选项发生相应变化。

    初始化代码如下(示例):

    /*设置搜索框*/
    	QListWidgetItem* currentItem = new QListWidgetItem(list_widget_);
    	//设置搜索框提示信息
    	search_bar_->setPlaceholderText("Search.........");
    	//显示清除按钮
    	search_bar_->setClearButtonEnabled(true);
    	list_widget_->addItem(currentItem);
    	list_widget_->setItemWidget(currentItem, search_bar_);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输入文本变化槽函数代码如下(示例):

    connect(search_bar_, SIGNAL(textChanged(const QString&)), this, SLOT(onSearch(const QString&)));
    
    
    void MultiSelectComboBox::onSearch(const QString& _text)
    {
    	for (int i = 1; i < list_widget_->count(); i++)
    	{
    		QCheckBox *check_box = static_cast(list_widget_->itemWidget(list_widget_->item(i)));
            //文本匹配则显示,反之隐藏
    		//Qt::CaseInsensitive模糊查询
    		if (check_box->text().contains(_text, Qt::CaseInsensitive))
    			list_widget_->item(i)->setHidden(false);
    		else
    			list_widget_->item(i)->setHidden(true);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2. 选项部分

    单击选项选中,选中后更新line_edit_内容,绑定stateChanged信号,鼠标单击选项时时,就会将所有选项遍历一遍,然后把选中的选项组织为字符串,更新到line_edit_上。发送信号void selectionChange(const QString _data);,此信号为选中选项发送变化时发出。

    选项变化槽函数代码如下(示例):

    connect(checkbox, &QCheckBox::stateChanged, this, &MultiSelectComboBox::stateChange);
    
    
    void MultiSelectComboBox::stateChange(int _row)
    {
    	Q_UNUSED(_row);
    	QString selected_data("");
    	int count = list_widget_->count();
    	for (int i = 1; i < count; i++)
    	{
    		QWidget *widget = list_widget_->itemWidget(list_widget_->item(i));
    		QCheckBox *check_box = static_cast(widget);
    		if (check_box->isChecked())
    		{
    			selected_data.append(check_box->text()).append(";");
    		}
    	}
    	selected_data.chop(1);
    	if (!selected_data.isEmpty())
    	{
    		line_edit_->setText(selected_data);
    	}
    	else
    	{
    		line_edit_->clear();
    	}
    	line_edit_->setToolTip(selected_data);
    	emit selectionChange(selected_data);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4. 对外接口定义

    考虑到使用方便,这里定义了一些接口方便用户使用,接口含义见下方注释 (文本框代表line_edit_)。

    接口定义如下(示例):

        //隐藏下拉框
    	virtual void hidePopup();
    
    	//添加一条选项
    	void addItem(const QString& _text, const QVariant& _variant = QVariant());
    
    	//添加多条选项
    	void addItems(const QStringList& _text_list);
    
    	//返回当前选中选项
    	QStringList currentText();
    
    	//返回当前选项条数
    	int count()const;
    
    	//设置搜索框默认文字
    	void SetSearchBarPlaceHolderText(const QString _text);
    
    	//设置文本框默认文字
    	void SetPlaceHolderText(const QString& _text);
    	
    	//下拉框状态恢复默认(所有选项都恢复为未选中状态)
    	void ResetSelection();
    
    	//清空所有内容(选项内容全部清空)
    	void clear();
    
    	//文本框内容清空(选项内容不清空,所有选项都恢复为未选中状态,文本框清空)
    	void TextClear();
    
    	//设置选中文本--单
    	void setCurrentText(const QString& _text);
    
    	//设置选中文本--多
    	void setCurrentText(const QStringList& _text_list);
    
    	//设置搜索框是否禁用
    	void SetSearchBarHidden(bool _flag);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    5. 代码实现

    头文件:

    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    
    class MultiSelectComboBox : public QComboBox
    {
    	Q_OBJECT
    
    public:
    	MultiSelectComboBox(QWidget *parent = Q_NULLPTR);
    	~MultiSelectComboBox();
    
    	//隐藏下拉框
    	virtual void hidePopup();
    
    	//添加一条选项
    	void addItem(const QString& _text, const QVariant& _variant = QVariant());
    
    	//添加多条选项
    	void addItems(const QStringList& _text_list);
    
    	//返回当前选中选项
    	QStringList currentText();
    
    	//返回当前选项条数
    	int count()const;
    
    	//设置搜索框默认文字
    	void SetSearchBarPlaceHolderText(const QString _text);
    
    	//设置文本框默认文字
    	void SetPlaceHolderText(const QString& _text);
    	
    	//下拉框状态恢复默认
    	void ResetSelection();
    
    	//清空所有内容
    	void clear();
    
    	//文本框内容清空
    	void TextClear();
    
    	//设置选中文本--单
    	void setCurrentText(const QString& _text);
    
    	//设置选中文本--多
    	void setCurrentText(const QStringList& _text_list);
    
    	//设置搜索框是否禁用
    	void SetSearchBarHidden(bool _flag);
    
    protected:
    
    	//事件过滤器
    	virtual bool eventFilter(QObject *watched,QEvent *event);
    
    	//滚轮事件
    	virtual void wheelEvent(QWheelEvent *event);
    
    	//按键事件
    	virtual void keyPressEvent(QKeyEvent *event);
    
    private slots:
    
    	//槽函数:文本框文本变化
    	void stateChange(int _row);
    
    	//槽函数:搜索框文本变化
    	void onSearch(const QString& _text);
    
    	//槽函数:点击下拉框选项
    	void itemClicked(int _index);
    
    signals:
    
    	//信号:发送当前选中选项
    	void selectionChange(const QString _data);
    
    private:
    	//下拉框
    	QListWidget* list_widget_;
    	//文本框,搜索框
    	QLineEdit* line_edit_, *search_bar_;
    	//搜索框显示标志
    	bool hidden_flag_;
    	//下拉框显示标志
    	bool show_flag_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    实现:

    #include "multi_select_combobox.h"
    
    MultiSelectComboBox::MultiSelectComboBox(QWidget *parent)
    	: QComboBox(parent)
    	, hidden_flag_(true)
    	, show_flag_(false)
    {
    	list_widget_ = new QListWidget();
    	line_edit_ = new QLineEdit();
    	search_bar_ = new QLineEdit();
    
    	/*设置搜索框*/
    	QListWidgetItem* currentItem = new QListWidgetItem(list_widget_);
    	//设置搜索框提示信息
    	search_bar_->setPlaceholderText("Search.........");
    	//显示清除按钮
    	search_bar_->setClearButtonEnabled(true);
    	list_widget_->addItem(currentItem);
    	list_widget_->setItemWidget(currentItem, search_bar_);
    
    	/*设置文本框*/
    	//设为只读,因为该输入框只用来显示选中的选项,称为文本框更合适些
    	line_edit_->setReadOnly(true);
    	//把当前对象安装(或注册)为事件过滤器,当前也称为过滤器对象。事件过滤器通常在构造函数中进行注册。
    	line_edit_->installEventFilter(this);
    	//设置禁用样式,因为不受样式表控制,临时这样解决
    	line_edit_->setStyleSheet("QLineEdit:disabled{background:rgb(233,233,233);}");
    
    	this->setModel(list_widget_->model());
    	this->setView(list_widget_);
    	this->setLineEdit(line_edit_);
    	connect(search_bar_, SIGNAL(textChanged(const QString&)), this, SLOT(onSearch(const QString&)));
    	connect(this, static_cast(&QComboBox::activated), this, &MultiSelectComboBox::itemClicked);
    }
    
    MultiSelectComboBox::~MultiSelectComboBox()
    {
    }
    
    void MultiSelectComboBox::hidePopup()
    {
    	show_flag_ = false;
    	int width = this->width();
    	int height = this->height();
    	int x = QCursor::pos().x() - mapToGlobal(geometry().topLeft()).x() + geometry().x();
    	int y = QCursor::pos().y() - mapToGlobal(geometry().topLeft()).y() + geometry().y();
    	if (x >= 0 && x <= width && y >= this->height() && y <= height + this->height())
    	{
    	}
    	else
    	{
    		QComboBox::hidePopup();
    	}
    }
    
    void MultiSelectComboBox::addItem(const QString& _text, const QVariant& _variant /*= QVariant()*/)
    {
    	Q_UNUSED(_variant);
    	QListWidgetItem* item = new QListWidgetItem(list_widget_);
    	QCheckBox* checkbox = new QCheckBox(this);
    	checkbox->setText(_text);
    	list_widget_->addItem(item);
    	list_widget_->setItemWidget(item, checkbox);
    	connect(checkbox, &QCheckBox::stateChanged, this, &MultiSelectComboBox::stateChange);
    }
    
    void MultiSelectComboBox::addItems(const QStringList& _text_list)
    {
    	for (const auto& text_one : _text_list)
    	{
    		addItem(text_one);
    	}
    }
    
    QStringList MultiSelectComboBox::currentText()
    {
    	QStringList text_list;
    	if (!line_edit_->text().isEmpty())
    	{
    		//以;为分隔符分割字符串
    		text_list = line_edit_->text().split(':');
    	}
    	return text_list;
    }
    
    int MultiSelectComboBox::count() const
    {
    	int count = list_widget_->count() - 1;
    	if (count < 0)
    	{
    		count = 0;
    	}
    	return count;
    }
    
    void MultiSelectComboBox::SetSearchBarPlaceHolderText(const QString _text)
    {
    	search_bar_->setPlaceholderText(_text);
    }
    
    void MultiSelectComboBox::SetPlaceHolderText(const QString& _text)
    {
    	line_edit_->setPlaceholderText(_text);
    }
    
    void MultiSelectComboBox::ResetSelection()
    {
    	int count = list_widget_->count();
    	for (int i = 1; i < count; i++)
    	{
    		//获取对应位置的QWidget对象
    		QWidget *widget = list_widget_->itemWidget(list_widget_->item(i));
    		//将QWidget对象转换成对应的类型
    		QCheckBox *check_box = static_cast(widget);
    		check_box->setChecked(false);
    	}
    }
    
    void MultiSelectComboBox::clear()
    {
    	line_edit_->clear();
    	list_widget_->clear();
    	QListWidgetItem* currentItem = new QListWidgetItem(list_widget_);
    	search_bar_->setPlaceholderText("Search.........");
    	search_bar_->setClearButtonEnabled(true);
    	list_widget_->addItem(currentItem);
    	list_widget_->setItemWidget(currentItem, search_bar_);
    	SetSearchBarHidden(hidden_flag_);
    	connect(search_bar_, SIGNAL(textChanged(const QString&)), this, SLOT(onSearch(const QString&)));
    }
    
    void MultiSelectComboBox::TextClear()
    {
    	line_edit_->clear();
    	ResetSelection();
    }
    
    void MultiSelectComboBox::setCurrentText(const QString& _text)
    {
    	int count = list_widget_->count();
    	for (int i = 1; i < count; i++)
    	{
    		//获取对应位置的QWidget对象
    		QWidget *widget = list_widget_->itemWidget(list_widget_->item(i));
    		//将QWidget对象转换成对应的类型
    		QCheckBox *check_box = static_cast(widget);
    		if (_text.compare(check_box->text()))
    			check_box->setChecked(true);
    	}
    }
    
    void MultiSelectComboBox::setCurrentText(const QStringList& _text_list)
    {
    	int count = list_widget_->count();
    	for (int i = 1; i < count; i++)
    	{
    		//获取对应位置的QWidget对象
    		QWidget *widget = list_widget_->itemWidget(list_widget_->item(i));
    		//将QWidget对象转换成对应的类型
    		QCheckBox *check_box = static_cast(widget);
    		if (_text_list.contains(check_box->text()))
    			check_box->setChecked(true);	
    	}
    }
    
    void MultiSelectComboBox::SetSearchBarHidden(bool _flag)
    {
    	hidden_flag_ = _flag;
    	list_widget_->item(0)->setHidden(hidden_flag_);
    }
    
    bool MultiSelectComboBox::eventFilter(QObject *watched, QEvent *event)
    {
    	//设置点击输入框也可以弹出下拉框
    	if (watched == line_edit_ && event->type() == QEvent::MouseButtonRelease && this->isEnabled())
    	{
    		showPopup();
    		return true;
    	}
    	return false;
    }
    
    void MultiSelectComboBox::wheelEvent(QWheelEvent *event)
    {
    	//禁用QComboBox默认的滚轮事件
    	Q_UNUSED(event);
    }
    
    void MultiSelectComboBox::keyPressEvent(QKeyEvent *event)
    {
    	QComboBox::keyPressEvent(event);
    }
    
    void MultiSelectComboBox::stateChange(int _row)
    {
    	Q_UNUSED(_row);
    	QString selected_data("");
    	int count = list_widget_->count();
    	for (int i = 1; i < count; i++)
    	{
    		QWidget *widget = list_widget_->itemWidget(list_widget_->item(i));
    		QCheckBox *check_box = static_cast(widget);
    		if (check_box->isChecked())
    		{
    			selected_data.append(check_box->text()).append(";");
    		}
    	}
    	selected_data.chop(1);
    	if (!selected_data.isEmpty())
    	{
    		line_edit_->setText(selected_data);
    	}
    	else
    	{
    		line_edit_->clear();
    	}
    	line_edit_->setToolTip(selected_data);
    	emit selectionChange(selected_data);
    }
    
    void MultiSelectComboBox::onSearch(const QString& _text)
    {
    	for (int i = 1; i < list_widget_->count(); i++)
    	{
    		QCheckBox *check_box = static_cast(list_widget_->itemWidget(list_widget_->item(i)));
    		//文本匹配则显示,反之隐藏
    		//Qt::CaseInsensitive模糊查询
    		if (check_box->text().contains(_text, Qt::CaseInsensitive))
    			list_widget_->item(i)->setHidden(false);
    		else
    			list_widget_->item(i)->setHidden(true);
    	}
    }
    
    void MultiSelectComboBox::itemClicked(int _index)
    {
    	if (_index != 0)
    	{
    		QCheckBox *check_box = static_cast(list_widget_->itemWidget(list_widget_->item(_index)));
    		check_box->setChecked(!check_box->isChecked());
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242

    总结

    通过这次制作下拉复选框,深入的了解了QComboBox的组成。
    存在问题:1、输入框不能输入中文。
    2、输入框点击后,再次点击不能隐藏下拉框
    3、点击item空白部分会直接隐藏下拉框
    优化:样式优化,后续有时间优化后会继续更新。

    如果此文帮助到你( ω ),动动小手点个赞可好O(∩_∩)O。

    原创文章,转载请标明本文出处。

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    检测Nginx配置是否正确
    算法刷题日志——回溯算法
    java计算机毕业设计快递物流管理源程序+mysql+系统+lw文档+远程调试
    【OPENVX】对象基本使用之vx_lut
    YOLOv8蒸馏 | 知识蒸馏 | 利用模型蒸馏改进YOLOv8进行无损涨点 | MimicLoss(在线蒸馏 + 离线蒸馏)
    [SystemC]Primitive Channels and the Kernel
    LeetCode【65. 有效数字】
    栈的顺序存储实现(C语言)(数据结构与算法)
    DDD领域驱动设计-实体
    使用JAXB将xml转成Java对象
  • 原文地址:https://blog.csdn.net/Bejpse/article/details/126113951