教你如何实现带复选框的ComboBox(自定义QComboBox)

2023-10-30

Qt提供的QComboBox只能选择其中一个选项,无法实现同时选中多个,而实际工程项目中,下拉框中带复选框的需求比比皆是。阅读了网上大量的博客,但是没有发现一个能完美的实现该功能的ComboBox,都存在各种未解决的bug缺陷,样子是那么回事儿,但是里子经不起推敲。经过笔者多次熬夜,接近通宵,工程中反复使用、测试,bug逐渐被解决。

请读者转载时,注明来源,谢谢!!!

废话不多说,下面就教大家如何实现。

一、效果图

1、初始化样式

2、展开样式

3、选择多个item的样式

4、选择后的样式,鼠标hover时,显示被选中的item文本

二、源码展示

1、头文件XComboBox.h

#pragma once

#include <QComboBox>
#include <QStandardItemModel>
#include <QKeyEvent>
#include <QListView>

class QLineEdit;

struct ItemInfo
{
    int idx;
    QString str;
    QVariant userData;
    bool bChecked;

    ItemInfo()
    {
        idx = -1;
        str = QString("");
        userData = QVariant();
        bChecked = false;
    }
};

class KeyPressEater : public QObject
{
    Q_OBJECT
public:
    KeyPressEater(QObject* parent = nullptr) :QObject(parent) {}
    ~KeyPressEater() {}

signals:
    void sigActivated(int idx);

protected:
    bool eventFilter(QObject *obj, QEvent *event)
    {
        if (event->type() == QEvent::KeyPress)
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Space)
            {
                QListView* lstV = qobject_cast<QListView*>(obj);
                if (nullptr != lstV)
                {
                    int idx = lstV->currentIndex().row();
                    if (-1 != idx)
                    {
                        emit sigActivated(idx);
                    }
                }
            }
            else if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)
            {
                return QObject::eventFilter(obj, event);
            }

            return true;
        }
        else {
            // standard event processing
            return QObject::eventFilter(obj, event);
        }
    }
};

class XComboBox : public QComboBox
{
    Q_OBJECT

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

    // 添加item
    void AddItem(const QString& str, bool bChecked = false, QVariant &userData = QVariant());
    void AddItems(const QList<ItemInfo>& lstItemInfo);
    void AddItems(const QMap<QString, bool>& mapStrChk);
    void AddItems(const QList<QString>& lstStr);
    // 删除item
    void RemoveItem(int idx);
    // 清空item
    void Clear();
    // 获取选中的数据字符串列表
    QStringList GetSelItemsText();
    // 获取选中item的信息
    QList<ItemInfo> GetSelItemsInfo();
    // 获取item文本
    QString GetItemText(int idx);
    // 获取item信息
    ItemInfo GetItemInfo(int idx);

    // 查找目标项,找到则返回所在行号,否则返回-1
    int FindItem(const QVariant &Key) const;

signals:
    // popup显示信号
    void showingPopup();
    // popup隐藏信号
    void hidingPopup();
    // item选中状态发生变化
    void sigItemStateChanged();

protected:
    void showPopup();
    // 重写QComboBox的hidePopup函数
    // 目的选择过程中,不隐藏listview
    void hidePopup();
    virtual void mousePressEvent(QMouseEvent * event);
    virtual void mouseReleaseEvent(QMouseEvent * event);
    virtual void mouseMoveEvent(QMouseEvent * event);

private:
    void UpdateText();

    private slots:
    void sltActivated(int idx);

private:
    QLineEdit* pLineEdit;
    QListView* pListView;
    QStandardItemModel m_model;
};

2、源文件XComboBox.cpp

#include "XComboBox.h"
#include <QLineEdit>
#include <QMouseEvent>

XComboBox::XComboBox(QWidget *parent)
    : QComboBox(parent)
{
    pLineEdit = new QLineEdit(this);
    pLineEdit->setReadOnly(true);
    this->setLineEdit(pLineEdit);
    this->lineEdit()->disconnect();

    KeyPressEater *keyPressEater = new KeyPressEater(this);
    pListView = new QListView(this);
    pListView->installEventFilter(keyPressEater);
    this->setView(pListView);

    this->setModel(&m_model);

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

XComboBox::~XComboBox()
{
}

void XComboBox::AddItem(const QString& str, bool bChecked /*= false*/, QVariant &userData /*= QVariant()*/)
{
    QStandardItem* item = new QStandardItem(str);
    item->setCheckable(true);
    item->setCheckState(bChecked ? Qt::Checked : Qt::Unchecked);
    item->setData(userData, Qt::UserRole + 1);
    m_model.appendRow(item);

    UpdateText();
}

void XComboBox::AddItems(const QList<ItemInfo>& lstItemInfo)
{
    for (auto a : lstItemInfo)
    {
        AddItem(a.str, a.bChecked, a.userData);
    }
}

void XComboBox::AddItems(const QMap<QString, bool>& mapStrChk)
{
    for (auto it = mapStrChk.begin(); it != mapStrChk.end(); ++it)
    {
        AddItem(it.key(), it.value());
    }
}

void XComboBox::AddItems(const QList<QString>& lstStr)
{
    for (auto a : lstStr)
    {
        AddItem(a, false);
    }
}

void XComboBox::RemoveItem(int idx)
{
    m_model.removeRow(idx);
    UpdateText();
}

void XComboBox::Clear()
{
    m_model.clear();
    UpdateText();
}

QStringList XComboBox::GetSelItemsText()
{
    QStringList lst;
    QString str = pLineEdit->text();
    if (str.isEmpty())
    {
        return lst;
    }
    else
    {
        lst = pLineEdit->text().split(",");
        return lst;
    }
}

QList<ItemInfo> XComboBox::GetSelItemsInfo()
{
    QList<ItemInfo> lstInfo;
    for (int i = 0; i < m_model.rowCount(); i++)
    {
        QStandardItem* item = m_model.item(i);
        if (item->checkState() == Qt::Unchecked) continue;

        ItemInfo info;
        info.idx = i;
        info.str = item->text();
        info.bChecked = true;
        info.userData = item->data(Qt::UserRole + 1);

        lstInfo << info;
    }

    return lstInfo;
}

QString XComboBox::GetItemText(int idx)
{
    if (idx < 0 || idx >= m_model.rowCount())
    {
        return QString("");
    }

    return m_model.item(idx)->text();
}

ItemInfo XComboBox::GetItemInfo(int idx)
{
    ItemInfo info;
    if (idx < 0 || idx >= m_model.rowCount())
    {
        return info;
    }

    QStandardItem* item = m_model.item(idx);
    info.idx = idx;
    info.str = item->text();
    info.bChecked = (item->checkState() == Qt::Checked);
    info.userData = item->data(Qt::UserRole + 1);

    return info;
}

// 查找目标项,找到则返回所在行号,否则返回-1
int XComboBox::FindItem(const QVariant &Key) const
{
    for (int nRow = 0; nRow < m_model.rowCount(); nRow++)
    {
        QStandardItem *pItem = m_model.item(nRow);
        if (pItem == nullptr)
        {
            continue;
        }

        if (pItem->data(Qt::UserRole + 1) == Key)
        {
            return nRow;
        }
    }

    return -1;
}

void XComboBox::showPopup()
{
    emit showingPopup();
    QComboBox::showPopup();
}

void XComboBox::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))
    {
        emit hidingPopup();
        QComboBox::hidePopup();
    }
}

void XComboBox::mousePressEvent(QMouseEvent * event)
{
    QComboBox::mousePressEvent(event);
    event->accept();
}

void XComboBox::mouseReleaseEvent(QMouseEvent * event)
{
    QComboBox::mouseReleaseEvent(event);
    event->accept();
}

void XComboBox::mouseMoveEvent(QMouseEvent * event)
{
    QComboBox::mouseMoveEvent(event);
    event->accept();
}

void XComboBox::UpdateText()
{
    QStringList lstTxt;
    for (int i = 0; i < m_model.rowCount(); ++i)
    {
        QStandardItem* item = m_model.item(i);
        if (item->checkState() == Qt::Unchecked) continue;

        lstTxt << item->text();
    }

    pLineEdit->setText(lstTxt.join(","));
    pLineEdit->setToolTip(lstTxt.join("\n"));
}

void XComboBox::sltActivated(int idx)
{
    QStandardItem* item = m_model.item(idx);
    if (nullptr == item) return;

    Qt::CheckState state = (item->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
    item->setCheckState(state);

    UpdateText();
    sigItemStateChanged();
}

3、测试代码

QStringList lstStr;
for (int i=0; i<10; ++i)
{
    lstStr << QString("item %1").arg(i);
}

ui.cmb->AddItems(lstStr);

三、重难点

如何避免原生QComboBox内部信号对自定的XComboBox的影响

四、源码下载链接如下

XComboBox.rar_C#如何在combobox中加入checkbox-C++文档类资源-CSDN下载

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

教你如何实现带复选框的ComboBox(自定义QComboBox) 的相关文章

随机推荐

  • 后缀数组(至少重复k次的可重叠的最长重复子串)—— POJ 3882

    对应POJ 题目 点击打开链接 Stammering Aliens Time Limit 3000MS Memory Limit 0KB 64bit IO Format lld llu Submit Status Description D
  • Windows下如何构建和发布Python模块

    转自 http blog sciencenet cn blog 800737 649292 html 1 首先将你要发布的模块 函数 写在一个Python文件里 即以 py的文件 如nester py文件 该文件内容即为你要发布的函数 2
  • RestClient操作文档和DSL查询语法

    一 文档操作 1 新增文档 本案例中 hotel为索引库名 61083为文档id Test void testAddDocument throws IOException 1 根据id查询酒店数据 Hotel hotel hotelServ
  • jQuery Custom PopUp Window

  • Mongo6.0 分片集群搭建

    Mongo6 0 分片搭建 6 5 1 分片 配置config server replica set 配置Shard Replica Sets 配置 mongos for the Sharded Cluster 分片设置 Range分区 H
  • 替代notepad++最优选择之一:vscode

    notepad 作者作妖不是一次两次了 实际上我的一些体制内或者有要求的大公司的朋友 都又在要求替换notepad 我之前没有选择替换notepad 主要是习惯了其操作方式和插件化 很多同类的工具一般有以下三类毛病 1 收费 2 功能少 不
  • 钉钉桌面版(dingtalk)介绍:支持Linux、Windows和macOS平台

    钉钉桌面版 dingtalk 介绍 支持Linux Windows和macOS平台 钉钉桌面版 dingtalk 它基于electron及钉钉网页版开发的跨平台桌面版钉钉 支持Linux Windows和macOS平台 以下是关于钉钉桌面版
  • Flutter网络请求与JSON解析

    本文介绍如何在Flutter中创建HTTP网络请求和对请求的json string进行类型解析 网络请求 官方使用的是用dart io中的HttpClient发起的请求 但HttpClient本身功能较弱 很多常用功能都不支持 建议使用di
  • 深度学习框架选择

    介绍 开源已经成为一种深度学习框架能否流行的唯一途径 如果你有需要 上GitHub就可以找到他们的源码和文档 深度学习框架比较 Keras 受到 Torch 启发 Keras 提供了简单易用的 API 接口 特别适合初学者入门 其后端采用
  • docker --help 命令

    Usage docker COMMAND A self sufficient runtime for containers Options config string Location of client config files defa
  • 系统安装部署系列教程(一):安装原版系统镜像

    电脑在使用过程中难免遇到问题 其中最万能 并不是一定有用 的一种方法就是重装系统了 很多人都不会安装系统 有时候甚至需要付费让别人来安装 其实安装系统这个过程并不算太难 我会慢慢向大家介绍安装系统的各种方法 首先要说的第一种自然是安装原版系
  • 超好用的pdf编辑+pdf转word工具 – Adobe Acrobat Pro DC下载

    Adobe Acrobat DC 是一款 PDF 的处理工具 下面我来给大家说一说这款软件相关内容 Adobe Acrobat Pro DC 是一款由 Adobe 官方推出的 PDF 编辑和阅读软件 是目前互联网上最专业最优秀的桌面 pdf
  • JavaScript中为什么0.1+0.2 不等于 0.3?

    1 问题现状 0 1 0 2 0 3 这个等式的成立看起来是理所当然的 然而事实并非如此 这个属于JS运算中精度的缺失问题 所以0 1 0 2 0 3 2 问题原因 因为计算机硬件存储数据时 是以二进制 10101010 形式进行存储的 所
  • oceanbase的数据视图

    文章目录 一 OceanBas 系统视图 1 1 字典视图 1 1 1 Help 命令 1 1 2 SPM 相关 1 1 3 权限相关 1 1 4 调度程序 1 1 5 存储相关 1 1 6 时区相关 1 1 7 字符集 1 1 8 表和列
  • Spring Boot日志详解

    目录 1 日志的抽象与实现 2 配置文件 2 1 application properties 2 2 application properties与logback spring xml的优先级 3 logback spring xml标签
  • golang的xml解析

  • 第五章 变形

    文章目录 一 长宽表的变形 1 pivot 2 pivot table 练一练 END 3 melt 4 wide to long 二 索引的变形 1 stack与unstack 2 聚合与变形的关系 三 其他变形函数 1 crosstab
  • Vue.js的组件(一)全局组件和局部组件

    刚开始学习Vue js 记录下心得 所谓组件 在我看来 就相当于新建一个属于自己的标签 但是这个标签的功能很强大 可以有很多特殊的功能 组件可以全局声明 js Vue component my component button1 templ
  • 环县计算机培训班,庆阳中职学校排名前十

    甘肃省庆阳市西峰区陇东职业中等专业学校建于1988年 是一所国家级重点职业学校 中德合作项目学校 学校位于庆阳市西峰区董志镇南街 毗邻庆阳市南区开发区 市内1路公交车南终点站 交通便利 环境优美 学校占地面积152亩 校舍建筑面积3 62万
  • 教你如何实现带复选框的ComboBox(自定义QComboBox)

    Qt提供的QComboBox只能选择其中一个选项 无法实现同时选中多个 而实际工程项目中 下拉框中带复选框的需求比比皆是 阅读了网上大量的博客 但是没有发现一个能完美的实现该功能的ComboBox 都存在各种未解决的bug缺陷 样子是那么回