多选下拉框(改进版)

声明

  本篇博客所谓的改进是建立在下面这篇博客代码的基础上。感谢博主 梦醒梦起,希望这篇博客可以帮助大家。
  本次代码可以在qt5上运行

https://blog.csdn.net/qq_43793182/article/details/122080407

改进代码

头文件

#pragma once

#include <QComboBox>
#include <QListWidget>
#include <QLineEdit>
#include <QCheckBox>
#include <QEvent>
#include <QMap>
#include <QDebug>
#include <QMouseEvent>
#include <QPoint>


//用户数据结构体,是为了设置每个列表项的信息
struct STRUCT_CK_USER_DATA
{
     QString m_userdata;
};
Q_DECLARE_METATYPE(STRUCT_CK_USER_DATA)


//下拉框对勾选框的操作
enum SELECT_TYPE
{
    SELECT_TYPE_SEL,       //勾选
    SELECT_TYPE_REMOVESEL, //去除勾选
};




class MultiSelectComboBox : public QComboBox
{
    Q_OBJECT

public:
    explicit MultiSelectComboBox(QWidget *parent = Q_NULLPTR);
    ~MultiSelectComboBox();


    /*************************************************
    函数名称:  addItem
    功能描述:  添加一条checkbox选项
    参数传入:  _text  checkbox项显示的文字   _variant用户数据
    参数传出:
    返回值:
    其他:
    *************************************************/
    void addItem(const QString& _text, const QVariant& _variant = QVariant());

    /*************************************************
    函数名称:  currentText
    功能描述:  返回当前选中的checkbox项对应的文本
    参数传入:
    参数传出:
    返回值:    QStringList 选中项对应的本文
    其他:
    *************************************************/
    QStringList currentText();

    /*************************************************
    函数名称:  count
    功能描述:  返回当前选中的checkbox项的总数
    参数传入:
    参数传出:
    返回值:    int 选中的checkbox项的总数
    其他:
    *************************************************/
    int count()const;

    /*************************************************
    函数名称:  SetSearchBarPlaceHolderText
    功能描述:  设置搜索文本框默认的显示内容
    参数传入:  _text  默认显示的文本
    参数传出:
    返回值:
    其他:
    *************************************************/
    void setSearchBarPlaceHolderText(const QString &_text);

    /*************************************************
    函数名称:  SetPlaceHolderText
    功能描述:  设置文本框默认显示的内容
    参数传入:  _text  默认显示的文本
    参数传出:
    返回值:
    其他:
    *************************************************/
    void setPlaceHolderText(const QString& _text);

    /*************************************************
    函数名称:  ResetSelection
    功能描述:  下拉框中的所有checkbox恢复至不选择状态 而且所对应的用户数据设置为空,同时清空文本显示框
    参数传入:
    参数传出:
    返回值:
    其他:
    *************************************************/
    void resetSelection();

    /*************************************************
    函数名称:  clear
    功能描述:  清空下拉框中的所有checkbox控件
    参数传入:
    参数传出:
    返回值:
    其他:
    *************************************************/
    void clearCheckBox();


private:
    /*************************************************
    函数名称: hidePopup
    功能描述: 隐藏MultiSelectComboBox的下拉框界面
    参数传入:
    参数传出:
    返回值:
    其他:
    *************************************************/
    virtual void hidePopup();


    virtual void showPopup();

    /*************************************************
    函数名称: CanHide
    功能描述: 根据鼠标当前的坐标位置判断下拉框界面是否可隐藏,如果鼠标处在下拉框界面的内部则不能隐藏,反之可以隐藏
    参数传入:
    参数传出:
    返回值:  不能隐藏(false) 可以隐藏(true)
    其他:
    *************************************************/
    bool CanHide();


protected:

    //事件过滤器
    virtual bool eventFilter(QObject *watched,QEvent *event);

    //滚轮事件
    virtual void wheelEvent(QWheelEvent *event);

    //按键事件
    virtual void keyPressEvent(QKeyEvent *event);

private Q_SLOTS:

    /*************************************************
    函数名称:  stateChange
    功能描述:  响应checkbox项的状态变化
    参数传入:  _state checkbox的状态值
    参数传出:
    返回值:
    其他:
    *************************************************/
    void stateChange(int _state);

    /*************************************************
    函数名称:  onSearch
    功能描述:  响应搜索框内容的变化
    参数传入:  _text搜索框的内容
    参数传出:
    返回值:
    其他:
    *************************************************/
    void onSearch(const QString& _text);


    //槽函数:点击下拉框选项
    /*************************************************
    函数名称:  itemClicked
    功能描述:  响应下拉框选项点击
    参数传入:  _index 被点击项的索引
    参数传出:
    返回值:
    其他:
    *************************************************/
    void itemClicked(int _index);

Q_SIGNALS:

    /*************************************************
    函数名称:  SigSelChange
    功能描述:  信号:向外界发送数据
    参数传入:  SELECT_TYPE 勾选框操作类型(是勾选还是去除勾选)
              QString     用户数据 这个也可以定结构体
    参数传出:
    返回值:
    其他:
    *************************************************/
    void SigSelChange(SELECT_TYPE,QString);


private:

    //即将要添加的勾选框的索引值  因为下拉界面中可能会增加其他控件,这会对勾选框的索引值产生影响
    int m_iCheckBoxNextIndex;

    //勾选框起始索引
    int m_iCheckBoxStartIndex;

    //列表部件(下拉框部件)
    QListWidget * m_plistWgt;

    //文本框
    QLineEdit * m_plineEdt;

    //搜索框
    QLineEdit * m_plineSer;

    //按照点击顺序存储checkbox对象
    QVector<QCheckBox *> m_vecSelItem;
};

源文件

#if _MSC_VER >= 1600 //vs2010及其以上版本
#pragma execution_character_set("utf-8")  //这句是关键
#endif

#include "CLS_MultiSelectComboBox.h"

MultiSelectComboBox::MultiSelectComboBox(QWidget *parent)
    : QComboBox(parent)
    , m_iCheckBoxNextIndex(0)
    , m_iCheckBoxStartIndex(0)
{

    //创建显示文本框界面
    m_plineEdt  = new QLineEdit(this);
    m_plineEdt->setReadOnly(true);
    m_plineEdt->installEventFilter(this);

    //创建搜索文本框界面
    m_plineSer  = new QLineEdit(this);
    m_plineSer->setPlaceholderText(QStringLiteral("Search........."));
    m_plineSer->setClearButtonEnabled(true);
    m_plineSer->installEventFilter(this);


    //创建下拉列表界面
    m_plistWgt  = new QListWidget(this);

    /*设置搜索框*/
    QListWidgetItem* currentItem = new QListWidgetItem(m_plistWgt);
    m_plistWgt->addItem(currentItem);
    m_plistWgt->setItemWidget(currentItem, m_plineSer);

    this->setModel(m_plistWgt->model());
    this->setView(m_plistWgt);
    this->setLineEdit(m_plineEdt);
    this->setMaxVisibleItems(10);  //设置可显示项的最大数量
    this->installEventFilter(this);

    connect(m_plineSer, SIGNAL(textChanged(const QString&)), this, SLOT(onSearch(const QString&)));
    connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &MultiSelectComboBox::itemClicked);

    //设置样式表
    QFile file(QStringLiteral("./style.qss"));
    file.open(QIODevice::ReadOnly);
    this->setStyleSheet(QString::fromLatin1(file.readAll()));

    if(m_plineSer)
    {
        m_iCheckBoxNextIndex++;
        m_iCheckBoxStartIndex++;
    }
}

MultiSelectComboBox::~MultiSelectComboBox()
{

}

void MultiSelectComboBox::hidePopup()
{
     if(CanHide())
     {
          QComboBox::hidePopup();
     }
}

void MultiSelectComboBox::showPopup()
{
    QComboBox::showPopup();
}

void MultiSelectComboBox::addItem(const QString& _text, const QVariant& _variant)
{
    if(m_plistWgt == nullptr)
    {
        return;
    }

    //新增的列表项对象
    QListWidgetItem* item = nullptr;

    //获取当前的列表项数
    int count = m_plistWgt->count();
    if(count == m_iCheckBoxNextIndex)
    {//判断下索引和个数的关系是否匹配,注意此时新的checkbox还有添加到列表中

        item = new QListWidgetItem(m_plistWgt);
        STRUCT_CK_USER_DATA userdata = _variant.value<STRUCT_CK_USER_DATA>();
        item->setData(Qt::UserRole + m_iCheckBoxNextIndex,_variant);
    }

    QCheckBox * checkbox = new QCheckBox(this);
    checkbox->setText(_text);
    checkbox->installEventFilter(this);

    m_plistWgt->addItem(item);
    m_plistWgt->setItemWidget(item, checkbox);

    m_iCheckBoxNextIndex++;

    connect(checkbox, &QCheckBox::stateChanged, this, &MultiSelectComboBox::stateChange);
}

QStringList MultiSelectComboBox::currentText()
{
    QStringList text_list;

    if(m_plineEdt == nullptr)
    {
        return text_list;
    }

    if (!m_plineEdt->text().isEmpty())
    {
        //以;为分隔符分割字符串
        text_list = m_plineEdt->text().split(';');
    }
    return text_list;
}

int MultiSelectComboBox::count() const
{
    if(m_plistWgt == nullptr)
    {
        return 0;
    }

    int count = m_plistWgt->count() - m_iCheckBoxStartIndex;
    if (count < 0)
    {
        count = 0;
    }
    return count;
}

void MultiSelectComboBox::setSearchBarPlaceHolderText(const QString & _text)
{
    if(m_plineSer == nullptr)
    {
        return;
    }
    m_plineSer->setPlaceholderText(_text);
}

void MultiSelectComboBox::setPlaceHolderText(const QString & _text)
{
    if(m_plineEdt == nullptr)
    {
        return;
    }
    m_plineEdt->setPlaceholderText(_text);
}

void MultiSelectComboBox::resetSelection()
{
    if(m_plistWgt == nullptr || m_plineEdt == nullptr)
    {
        return;
    }

    m_plineEdt->clear();

    int count = m_plistWgt->count();
    for (int i = m_iCheckBoxStartIndex; i < count; i++)
    {
        QListWidgetItem * itemwgt = m_plistWgt->item(i);

        if(itemwgt)
        {
            //设置用户数据为空
            itemwgt->setData(Qt::UserRole,QVariant());

            QWidget *widget = m_plistWgt->itemWidget(itemwgt);

            if(widget)
            {
                QCheckBox *check_box = static_cast<QCheckBox*>(widget);
                if(check_box)
                {
                    check_box->setChecked(false);
                }
            }
        }
    }
}

void MultiSelectComboBox::clearCheckBox()
{
    if(m_plineEdt == nullptr || m_plistWgt == nullptr)
    {
        return;
    }

    //清空文本显示框内容
    m_plineEdt->clear();

    m_vecSelItem.clear();

    //清空下拉框中的checkbox控件
    int count = m_plistWgt->count();
    while (count != m_iCheckBoxStartIndex)
    {
        for (int i = m_iCheckBoxStartIndex; i < count; i++)
        {
            QListWidgetItem * itemwgt = m_plistWgt->item(i);

            QWidget * widget = m_plistWgt->itemWidget(m_plistWgt->item(i));

            if(widget)
            {
                QCheckBox * check_box = static_cast<QCheckBox*>(widget);
                disconnect(check_box, &QCheckBox::stateChanged, this, &MultiSelectComboBox::stateChange);
                delete check_box;
                check_box = nullptr;
            }

            if(itemwgt)
            {
               m_plistWgt->removeItemWidget(itemwgt);

               delete  itemwgt;
               itemwgt = nullptr;
            }
        }

        count = m_plistWgt->count();
    }


    m_iCheckBoxNextIndex = 0;
    m_iCheckBoxStartIndex = 0;

    if(m_plineSer)
    {
        m_iCheckBoxNextIndex++;
        m_iCheckBoxStartIndex++;
    }
}


bool MultiSelectComboBox::eventFilter(QObject * watched, QEvent * event)
{
    QFocusEvent * pfocusevent = (QFocusEvent *)event;
    if(watched == this && pfocusevent->reason() == Qt::PopupFocusReason)
    {//控件抓取/释放键盘焦点。
        if(event->type() == QEvent::FocusAboutToChange)
        {//打开下拉框
            showPopup();
        }
        else if(event->type() == QEvent::FocusIn)
        {//关闭下拉框
            hidePopup();
        }
        return true;
    }
    else if(event->type() == QEvent::ContextMenu)
    {//弹出菜单的事件不予理会
        return true;
    }
    else if(((QMouseEvent *)event)->button() == Qt::RightButton)
    {//弹出菜单的事件不予理会
        return true;
    }

    return QComboBox::eventFilter(watched, event);
}

void MultiSelectComboBox::wheelEvent(QWheelEvent *event)
{
    //禁用QComboBox默认的滚轮事件
    Q_UNUSED(event);
}

void MultiSelectComboBox::keyPressEvent(QKeyEvent *event)
{
    QComboBox::keyPressEvent(event);
}

void MultiSelectComboBox::stateChange(int state)
{
    //获取信号的发送者
    QObject* obj = sender();
    QCheckBox * psender = dynamic_cast<QCheckBox*>(obj);

    if(psender == nullptr || m_plistWgt == nullptr || m_plineEdt == nullptr)
    {
        return;
    }

    //获取用户数据
    int count = m_plistWgt->count();
    for (int i = m_iCheckBoxStartIndex; i < count; i++)
    {
        QWidget *widget = m_plistWgt->itemWidget(m_plistWgt->item(i));

        QVariant qvardata = m_plistWgt->item(i)->data(Qt::UserRole + i);

        STRUCT_CK_USER_DATA userdata = qvardata.value<STRUCT_CK_USER_DATA>();

        QCheckBox *check_box = static_cast<QCheckBox*>(widget);

        if (check_box && (psender == check_box))
        {
            if(state == Qt::Checked)
            {
                Q_EMIT SigSelChange(SELECT_TYPE_SEL,userdata.m_userdata);
            }
            else if(state == Qt::Unchecked)
            {
                Q_EMIT SigSelChange(SELECT_TYPE_REMOVESEL,userdata.m_userdata);
            }
            break;
        }
    }


    //根据选择操作,增加或删除checkbox对象
    if(psender)
    {
        if(state == Qt::Checked)
        {
            m_vecSelItem.push_back(psender);
        }
        else if(state == Qt::Unchecked)
        {
            for(int i = 0; i < m_vecSelItem.size(); ++i)
            {
                if(m_vecSelItem[i] && (m_vecSelItem[i] == psender))
                {
                    m_vecSelItem.remove(i); //删除对应的checkbox对象
                }
            }
        }
    }

    //每次都清空一下文本框
    m_plineEdt->clear();

    //重新显示文本框内容
    QString qstr;
    for(int i = 0; i < m_vecSelItem.size(); ++i)
    {
        qstr += m_vecSelItem[i]->text() + ";";
    }

    m_plineEdt->setText(qstr);
    m_plineEdt->setToolTip(m_plineEdt->text());
}

void MultiSelectComboBox::onSearch(const QString& _text)
{
    if(m_plistWgt == nullptr)
    {
        return;
    }

    for (int i = 1; i < m_plistWgt->count(); i++)
    {
        QCheckBox *check_box = static_cast<QCheckBox*>(m_plistWgt->itemWidget(m_plistWgt->item(i)));
        //文本匹配则显示,反之隐藏
        //Qt::CaseInsensitive模糊查询
        if (check_box->text().contains(_text, Qt::CaseInsensitive)) {
            m_plistWgt->item(i)->setHidden(false);
        }
        else
        {
            m_plistWgt->item(i)->setHidden(true);
        }
    }
}

void MultiSelectComboBox::itemClicked(int _index)
{
    if(m_plistWgt == nullptr)
    {
        return;
    }

    if (_index != 0)
    {
        QCheckBox *check_box = static_cast<QCheckBox*>(m_plistWgt->itemWidget(m_plistWgt->item(_index)));
        check_box->setChecked(!check_box->isChecked());
    }
}

bool MultiSelectComboBox::CanHide()
{
    if(m_plistWgt == nullptr)
    {
        return false;
    }

    if(m_plistWgt)
    {
        QPoint topleftpos = this->mapToGlobal(m_plistWgt->geometry().topLeft());
        int topleftx = topleftpos.x();
        int toplefty = topleftpos.y() + m_plineEdt->height();
        int bottomrightx = topleftx + m_plistWgt->width();
        int bottomrighty = toplefty + m_plistWgt->height();

        QPoint mousepos = QCursor::pos();

        if(mousepos.x() < topleftx  || mousepos.x() > bottomrightx || mousepos.y() < toplefty  || mousepos.y() > bottomrighty)
        {//鼠标在下拉框界面外面
            return true;
        }
    }
    return false;
}



改进功能

  1. 增加用户数据,下拉框中的项可以增加用户数据。
  2. combobox的文本显示框会按照选择顺序显示选择项文本。
  3. 增加一些安全判断、优化注释。
  4. 增加样式表,但是并不是所有控件都增加了样式表。

2022-7-9修改记录

  1. 修改了清空下拉框元素后,再次添加崩溃的问题
  2. 修改了下拉框弹出的操作,只能通过点击下拉框箭头弹出,点击文本编辑框不能弹出下拉框

改进效果

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值