QAbstractItemView子类如:QTreeView、QTableView等子项单元格复选框勾选/取消勾选功能实现

目录

1.前言

2.需求提出

3.解决方法

4.后记


1.前言

本博文所说的技术点适用于同时满足下面条件的所有QAbstractItemView视图类的子类:

  • 模型类从 QAbstractItemModel派生。

  • 代理类从QStyledItemDelegate派生。

因为QColumnView、QHeaderView、QListView、QTableView、QTreeView、QListWidget 、QUndoView、QTableWidget、QTreeWidget都是从QAbstractItemView继承,故本博文所说的技术点也适用于这些类。

2.需求提出

基于Qt的model/view framework技术,利用QTreeView树视图实现业务,为QTreeView设置数据模型如下:


m_pLayerTreeView->setModel(m_pLayTreeModel);

其中为m_pLayerTreeView为QTreeView类对象;m_pLayTreeModel为CLayTreeModel类对象。CLayTreeModel类实现如下:


CLayTreeModel::CLayTreeModel(const CVectorLayerMap& mpLayer, QObject *parent)
    : QAbstractItemModel(parent)
{

}

QVariant CLayTreeModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const
{
    ......... // 其它代码略
   
    if (Qt::CheckStateRole == role) // 获取Qt::CheckStateRole角色
    {
         return pLayer->showOnBaseMap();
   }
}
    .........// 其它代码略

在上面代码中,通过获取Qt::CheckStateRole角色,树视图项中就会复选框出现,如下:

如果pLayer->showOnBaseMap()为true,则复选框呈勾选状态;反之则为取消勾选状态。

现在的问题是:如何实现鼠标单击勾选或取消勾选树视图中的复选框功能呢?

3.解决方法

构建一个从QStyledItemDelegate派生的代理类,将树视图对象的代理设置为该代理类对象:如下:


auto pLayerItemDelegate = new CLayerItemDelegate(this);
m_pLayerTreeView->setItemDelegate(pLayerItemDelegate);

CLayerItemDelegate类如下:


#include "layerItemDelegate.h"
#include<QDebug>
#include<QApplication>
#include<QMouseEvent>
CLayerItemDelegate::CLayerItemDelegate(QObject *parent)
    : QStyledItemDelegate(parent)
{
}

CLayerItemDelegate::~CLayerItemDelegate()
{
}

bool CLayerItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
    if (QEvent::MouseButtonPress != event->type()) // 不是鼠标单击事件,就返回
    {
        return QStyledItemDelegate::editorEvent(event, model, option, index);
    }

    auto pMouseEvent = dynamic_cast<QMouseEvent*>(event);
    if (Qt::LeftButton != pMouseEvent->button())  // 不是鼠标左键单击,就返回
    {
        return QStyledItemDelegate::editorEvent(event, model, option, index);
    }

    auto pt = pMouseEvent->pos();  // 鼠标点击树项的位置点(相对于树视图左上角顶点)
    auto itemRect = option.rect;   // 该树项在树视图占据的矩形位置(矩形坐标是相对树视图左上角顶点)

    auto nBtnMargin = qApp->style()->pixelMetric(QStyle::PM_ButtonMargin); // 复选框离该树项左侧或上侧的距离
    auto pDefaultIndicatorWidth = qApp->style()->pixelMetric(QStyle::PM_ButtonDefaultIndicator);// 复选框默认边框厚(宽)度

    auto nTwiceWidth = 2 * pDefaultIndicatorWidth; // 左右边框或上下边框,故需要乘2
   
  // 复选框宽度为:复选框指示器宽度加复选框左右边框厚(宽)度
    auto nIndicatorWidth = qApp->style()->pixelMetric(QStyle::PM_IndicatorWidth) + nTwiceWidth; // 复选框宽度

 // 复选框高度为:复选框指示器高度加复选框上下边框厚(宽)度
    auto nIndicatorHeight = qApp->style()->pixelMetric(QStyle::PM_IndicatorHeight) + nTwiceWidth; // 复选框高度

    auto nIndicatorTopX = itemRect.left() + nBtnMargin; // 复选框相对树项左上角的x坐标
    auto nIndicatorTopY = itemRect.top() + nBtnMargin;  // 复选框相对树项左上角的y坐标
    QRect indicatorRect(nIndicatorTopX, nIndicatorTopY, nIndicatorWidth, nIndicatorHeight); // 复选框在该树项占据的矩形
    auto bPtInRect = indicatorRect.contains(pt); // 鼠标在复选框占据的矩形内单击
    if (bPtInRect)
    {
        layerCheckBoxClickedSignal(index);
    }
    
    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

其中layerCheckBoxClickedSignal自定义的信号,参数为鼠标单击项的索引:


// 复选框被单击信号
void layerCheckBoxClickedSignal(const QModelIndex& index);

上面代码重载了editorEvent方法,当鼠标在树视图项上单击时,会进入到该函数,然后算出复选框在该树项的位置,看看鼠标单击位置是不是落在复选框在该树项占据的矩形内。如果是,就发送 layerCheckBoxClickedSignal信号,外层调用方捕捉该信号,在槽函数里将CLayTreeModel类data函数中影响Qt::CheckStateRole的布尔量设置为真或假,就可实现复选框的勾选或取消勾选。如下为代码:


connect(pLayerItemDelegate, &CLayerItemDelegate::layerCheckBoxClickedSignal, this, &CExquisiteGIS::layerCheckBoxClickedSlot);

void CExquisiteGIS::layerCheckBoxClickedSlot(const QModelIndex& index)
{
    // 获取该树项绑定的CVectorLayer类对象的指针
    auto pLayer = static_cast<CVectorLayer*>(index.internalPointer());
    if (nullptr == pLayer)
    {
        return;
    }

    // 调用该类对象的方法
    auto bShowBaseMap = pLayer->showOnBaseMap();
    pLayer->setShowOnBaseMap(!bShowBaseMap);

// 注意:一定要刷新,否则有时复选框勾选和取消勾选不能同步,即勾选(取消勾选)了依然没勾选(取消勾选)
    m_pLayerTreeView->update(); 
}

注意:一定要刷新,否则有时复选框勾选和取消勾选不能同步,即勾选(取消勾选)了依然没勾选(取消勾选)。当调用update()后会导致模型类CLayTreeModel的data函数被调用,从而实现界面复选框的刷新。

效果如下:

4.后记

上述功能,如果仅仅是为实现树视图,可以用QTreeWidget来实现。QTreeWidget类提供了对QCheckBox控件的函数和信号支持,不需要这么麻烦。另外也可以通过向QTreeView的树项插入QCheckBox控件来实现,具体参见《向QTableView、QTreeView单元格插入窗体小部件的功能实现》博文。需要说明的是:个人认为通过插入QCheckBox控件来实现,不够优雅;同时因为创建QCheckBox控件需要耗费内存,当树项很多时,在内存占用上没有前文不插入QCheckBox控件的方法来得优雅。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值