Qt之QTableView设置多列表头复选框(自定义QHeaderView)、单元格复选框(含源码+注释)

一、QTableView复选框示例图

下图为已设置好复选框的示例图,其中包含表头复选框和单元格复选框,源码在本文第三节(源码含详细注释)。
在这里插入图片描述
提示:不会使用Qt设计师设计界面的小伙伴点击这里

二、QTableView复选框

  1. 若是单元格设置复选框,则通过model对象获取指定位置的item,调用其setCheckable()函数即可;
  2. 但设置表头复选框则需要自定义表头类实现;
  3. 本文是通过QHeaderView的点击信号触发,要想发出该信号需在自定义表头类中调用setSectionsClickable开启鼠标点击,否则不发出对应的信号。

三、源码

3.1 CHeaderView(自定义表头类)

CHeaderView.h

#ifndef CHEADERVIEW_H
#define CHEADERVIEW_H

#include <QObject>
#include <QHeaderView>
#include <QMap>

class CHeaderView : public QHeaderView
{
    Q_OBJECT
public:
    /**
     * @brief CHeaderView 构造函数
     * @param orientation 方向
     * @param parent 父类对象
     */
    CHeaderView(Qt::Orientation orientation, QWidget *parent = nullptr);

    /**
     * @brief setColumnCheckable 设置指定列是否可选
     * @param column 指定列
     * @param checkable 可选值
     */
    void setColumnCheckable(int column, bool checkable);

signals:
    /**
     * @brief columnSectionClicked Section点击信号
     * @param logicalIndex 点击位置
     * @param checked 选中值
     */
    void columnSectionClicked(int logicalIndex, bool checked);

    // QHeaderView interface
protected:
    /**
     * @brief paintSection 绘制复选框
     * @param painter 绘制对象
     * @param rect 绘制区域
     * @param logicalIndex 当前索引位置
     */
    void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;

private:
    QMap<int, bool>    m_columpnCheckedMap; //包含复选框列的map容器

};

#endif // CHEADERVIEW_H

CHeaderView.cpp

#include "CHeaderView.h"
#include <QPainter>
#include <QCheckBox>

CHeaderView::CHeaderView(Qt::Orientation orientation, QWidget *parent)
    : QHeaderView(orientation, parent)
{
    //设置Section可点击,若不设置则不能发出sectionClicked信号
    this->setSectionsClickable(true);
    //当发出sectionClicked就进入匿名函数
    connect(this, &CHeaderView::sectionClicked, [=](int logicalIndex)
    {
        //判断map容器是否包含当前点击列,包含则更新并发出columnSectionClicked
        if(m_columpnCheckedMap.contains(logicalIndex))
        {
            //更新当前值
            m_columpnCheckedMap[logicalIndex] = !m_columpnCheckedMap[logicalIndex];
            //发出信号
            emit columnSectionClicked(logicalIndex, m_columpnCheckedMap[logicalIndex]);
        }
    });
}

void CHeaderView::setColumnCheckable(int column, bool checkable)
{
    //当可选值为true
    if(checkable)
    {
        //将指定列添加到map容器中
        m_columpnCheckedMap[column] = false;
    }
    else if(m_columpnCheckedMap.contains(column))   //当可选值为false,且map容器包含指定列
    {
        //移除指定列
        m_columpnCheckedMap.remove(column);
    }
}

void CHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
    painter->save();    //保存当前画笔状态
    QHeaderView::paintSection(painter, rect, logicalIndex);
    painter->restore(); //恢复保存的画笔状态

    //当map容器包含当前列才绘制复选框
    if(m_columpnCheckedMap.contains(logicalIndex))
    {
        //创建样式对象并设置区域
        QStyleOptionButton styleOption;
        styleOption.rect = rect.adjusted(3, 0, 0, 0);
        styleOption.state = QStyle::State_Enabled;

        //根据map中的值设置状态
        if(m_columpnCheckedMap[logicalIndex])
        {
            styleOption.state |= QStyle::State_On;
        }
        else
        {
            styleOption.state |= QStyle::State_Off;
        }

        //调用this的style对象绘制复选框
       this->style()->drawControl(QStyle::CE_CheckBox, &styleOption, painter);
    }
}


3.2 CMainWindow(主界面类)

CMainWindow.h

#ifndef CMAINWINDOW_H
#define CMAINWINDOW_H

#include <QMainWindow>
#include <QStandardItemModel>   //数据模型类
#include "CHeaderView.h"

namespace Ui {
class CMainWindow;
}

class CMainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit CMainWindow(QWidget *parent = 0);
    ~CMainWindow();

    /**
     * @brief setColumnCheckable 设置指定列是否可选(会一同设置model中同一列的单元格)
     * @param column 指定列
     * @param checkable 是否可选值
     */
    void setColumnCheckable(int column, bool checkable);

public slots:
    /**
     * @brief on_columnSectionClicked 列标题点击槽函数
     * @param column 所点击列
     * @param checked 点击列当前选中值
     */
    void on_columnSectionClicked(int column, bool checked);

private:
    Ui::CMainWindow     *ui;

    QStandardItemModel  *m_pModel;      //数据模型对象指针

    CHeaderView         *m_pHeaderView; //表头对象指针
};

#endif // CMAINWINDOW_H

CMainWindow.cpp

#include "CMainWindow.h"
#include "ui_CMainWindow.h"

CMainWindow::CMainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("QTableView简单使用");

    //建立模型对象空间并指定父对象
    m_pModel = new QStandardItemModel(ui->tableView);
    //添加列标题
    m_pModel->setHorizontalHeaderLabels(QStringList() << "one" << "two"
                                        << "three" << "four");
    //将数据模型设置到表对象上
    ui->tableView->setModel(m_pModel);


    //建立表头对象空间并指定父对象
    m_pHeaderView = new CHeaderView(Qt::Horizontal, ui->tableView);
    //将表对象的表头设为自定义表头
    ui->tableView->setHorizontalHeader(m_pHeaderView);
    //连接表头点击信号槽
    connect(m_pHeaderView, &CHeaderView::columnSectionClicked
            , this, &CMainWindow::on_columnSectionClicked);

    //循环添加三行数据
    for(int index = 0; index != 3; ++index)
    {
        m_pModel->appendRow(QList<QStandardItem *>()
                            << new QStandardItem(QString("第%1行,第0列").arg(index))
                            << new QStandardItem(QString("第%1行,第1列").arg(index))
                            << new QStandardItem(QString("第%1行,第2列").arg(index))
                            << new QStandardItem(QString("第%1行,第3列").arg(index)));
    }

    //设置第0列和第2列为可选
    this->setColumnCheckable(0, true);
    this->setColumnCheckable(2, true);
}

CMainWindow::~CMainWindow()
{
    //! 析构函数:
    //! 有些小伙伴会发现我没有析构model、headView对象,
    //! 那是因为我在获取对象空间的时候指定了父对象,
    //! 当其父对象析构时,会先析构其子对象为指针的对象。
    delete ui;
}

void CMainWindow::setColumnCheckable(int column, bool checkable)
{
    m_pHeaderView->setColumnCheckable(column, checkable);
    for(int row = 0; row != m_pModel->rowCount(); ++row)
    {
        m_pModel->item(row, column)->setCheckable(checkable);
    }
}

void CMainWindow::on_columnSectionClicked(int column, bool checked)
{
    //遍历行
    for(int row = 0; row != m_pModel->rowCount(); ++row)
    {
        //获取指定列当前行的item,根据checked设置选中状态
        if(checked)
        {
            m_pModel->item(row, column)->setCheckState(Qt::Checked);
        }
        else
        {
            m_pModel->item(row, column)->setCheckState(Qt::Unchecked);
        }
    }
}

总结

本文代码其实还缺少许多判断,如当获取的item为空、当所设置列已存在等情况的判断需要注意一下,其次是本文代码仅在表头复选框所在的表头单元格点击也可触发选中;并且在博主查阅相关文档时发现,大多数文章都重写了鼠标事件,因此在使用上可能和其他博主的有一定区别(不过本文代码和按钮代理的实现有一定类似哦)。
那今天就这样吧,晚安。

相关文章

Qt代理的实现(按钮篇,含源码+注释)
Qt代理的实现(常规控件篇,含源码+注释)
Qt之QTableView的简单使用(含源码+注释)
Qt之QSortFilterProxyModel的简单使用(QTableView搜索功能,含源码+注释)
Qt之QTreeView的简单使用(含源码+注释)
Qt之QListView的简单使用(含源码+注释)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除

  • 42
    点赞
  • 144
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lw向北.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值