向QAbstractItemView子类如:QTreeView、QTableView等子项单元格插入窗体小部件的功能实现(第2种方法)

目录

1.前言

2.实现详解

3.附加说明

3.1.窗体小部件相应鼠标事件的实现

3.2.普通窗体部件、复杂窗体部件绘制的不同说明

3.3.可绘制在单元格中的QStyleOption子类


1.前言

工作中经常会遇到这样的需求:向QAbstractItemView子类如QTreeView、QTableView单元格插入窗体小部件,如:进度条、按钮、单行编辑框等。下面链接的系列博文就是讲解如何实现该功能的。

  • 《向QAbstractItemView子类如:QTreeView、QTableView等子项单元格插入窗体小部件的功能实现(第1种方法)》。

  • 《向QAbstractItemView子类如:QTreeView、QTableView等子项单元格插入窗体小部件的功能实现(第2种方法)》。

  • 《向QAbstractItemView子类如:QTreeView、QTableView等子项单元格插入窗体小部件的功能实现(第3种方法)》。

这些系列博文所说的技术点适用于同时满足下面条件的所有类:

  • 模型类从 QAbstractItemModel派生。

  • 代理类从QStyledItemDelegate或QItemDelegate派生。

  • 视图类是QAbstractItemView的子类。

这些系列博文用到了Qt的model/view framework框架,如果对Qt的“模型/视图/代理”框架不懂,这些系列文章很难读懂。如果不懂这方面的知识,请在Qt Assistant 中输入Model/View Programming 学习了解。读者本机Qt安装目录下的Examples\Qt-XX.XX.XX\widgets\itemviews目录下有很多model/view framework的例子,可以进行自学了解,其中XX.XX.XX为Qt的版本号,如:5.14.1。

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

本博文通过Qt的QStyledItemDelegate或QItemDelegate类再结合QStyle类相关函数如:drawControl来实现向视图单元格插入窗体小部件功能。

2.实现详解

说明:下述所贴源码的.h文件请从cpp文件中自己抠出,不再贴.h文件。

以表格视图为例说明,其它从QAbstractItemView派生的子类视图和表格视图类似。先实现表格视图类的数据模型类,如下为model.cpp的实现:


#include "model.h"
CModel::CModel(QObject* parent)
    : QAbstractTableModel(parent)
{}

CModel::~CModel()
{}

// 作为例子,不设置每个项的数据
QVariant CModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const
{
    return QVariant();
}

// 作为例子,假想QTableView有3列
int CModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const
{
    return 3;
}

// 作为例子,假想QTableView有88行
int CModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const
{
    return 88;
}

QVariant CModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const
{
    if (orientation != Qt::Horizontal) // 作为例子演示,我们只关心表头是水平的情况
        return QVariant();

    if (role != Qt::DisplayRole)// 作为例子演示,我们只关心 Qt::DisplayRole
        return QVariant();

    // 构造列,第1列列名为"button";第2列列名为"checkbox",第3列列名为"Slider"
    if (0 == section)
        return "button";
    else if (1 == section)
        return "checkbox";
    else if (2 == section)
        return "Slider";

    return QVariant();
}

再实现表格视图的代理类,如下:


#include "tvItemDelegate.h"
#include  
#include
CTVItemDelegate::CTVItemDelegate(QObject* parent /*= nullptr*/)
{

}
CTVItemDelegate::~CTVItemDelegate()
{

}

void CTVItemDelegate::drawPushButton(QPainter* painter, const QStyleOptionViewItem& option, const QString&qsWndText) const
{
    QStyleOptionButton styleOptBtn;
    styleOptBtn.rect = option.rect; // 设置按钮占据的矩形
    styleOptBtn.icon = qApp->style()->standardIcon(QStyle::SP_DesktopIcon); // 设置按钮图标
    styleOptBtn.iconSize = QSize(32, 32);// 设置按钮图标尺寸
    styleOptBtn.text = QString("button%1").arg(qsWndText);// 设置按钮标题
    styleOptBtn.state = QStyle::State_Enabled | QStyle::State_Raised; // 设置按钮状态
    styleOptBtn.direction = Qt::LeftToRight; // 设置按钮水平布局,如果改为Qt::RightToLeft,则按钮图标在按钮标题右侧。
    styleOptBtn.features = QStyleOptionButton::None | QStyleOptionButton::Flat;// 设置按钮风格特点为普通扁平按钮
    qApp->style()->drawControl(QStyle::CE_PushButton, &styleOptBtn, painter); // 绘制按钮
}

void CTVItemDelegate::drawCheckBox(QPainter* painter, const QStyleOptionViewItem& option, const QString& qsWndText) const
{
    QStyleOptionButton styleOptBtn;
    styleOptBtn.rect = option.rect;// 设置按钮占据的矩形
    styleOptBtn.icon = qApp->style()->standardIcon(QStyle::SP_DesktopIcon);// 设置按钮图标
    styleOptBtn.iconSize = QSize(32, 32);
    styleOptBtn.text = QString("CheckBox%1").arg(qsWndText);// 设置复选按钮标题
    styleOptBtn.state = QStyle::State_Enabled | QStyle::State_Raised;// 设置按钮状态
    styleOptBtn.direction = Qt::LeftToRight; // 设置按钮水平布局,如果改为Qt::RightToLeft,则按钮图标在按钮标题右侧。
    styleOptBtn.features = QStyleOptionButton::None | QStyleOptionButton::Flat;// 设置按钮风格特点为普通扁平按钮
    qApp->style()->drawControl(QStyle::CE_CheckBox, &styleOptBtn, painter);// 绘制按钮
}

void CTVItemDelegate::drawSlider(QPainter* painter, const QStyleOptionViewItem& option) const
{
    QStyleOptionSlider styleOptnSlider;
    styleOptnSlider.rect = option.rect;// 设置按钮占据的矩形
    styleOptnSlider.state = QStyle::State_Enabled;// 设置按钮状态
    styleOptnSlider.minimum = 0; // 设置滑块最小值
    styleOptnSlider.maximum = 100;// 设置滑块最大值
    styleOptnSlider.sliderPosition = 50;// 设置滑块当前值
    qApp->style()->drawComplexControl(QStyle::CC_Slider, &styleOptnSlider, painter); // 绘制滑杆控件
}

void CTVItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (!index.isValid())
    {
        return;
    }

    auto nRowIndex = index.row();
    auto nColIndex = index.column();
    auto wndText = QString("%1").arg(nRowIndex);
    switch (nColIndex)
    {
    case 0:
    {
        drawPushButton(painter, option, wndText);
    }
    break;
    case 1:
    {
        drawCheckBox(painter, option, wndText);
    }
    break;
    case 2:
    {
        drawSlider(painter, option);
    }
    break;
    default:
        break;
    }
     
}

最后将设置表格视图模型、代理:


#include "QtWidgetsApplication1.h"
#include "model.h"
#include "tvItemDelegate.h"

QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    auto pModel = new CModel(this);

    // 为表视图设置数据模型
    ui.tableView->setModel(pModel); 

    // 为表视图设置代理类
    ui.tableView->setItemDelegate(new CTVItemDelegate(this));

    ui.tableView->setColumnWidth(0, 200);
    ui.tableView->setColumnWidth(1, 200);
    ui.tableView->setColumnWidth(2, 200);
}

效果如下:

向QAbstractItemView子类如:QTreeView、QTableView等子项单元格插入窗体小部件的功能实现(第2种方法)_第1张图片

3.附加说明

3.1.窗体小部件相应鼠标事件的实现

上述只是实现了插入窗体小部件,但发现按钮、滑杆等鼠标单击都没反应。这可以自己来实现,大体思路是:

  1. 重载代理类的如下函数:


[override virtual protected] bool QStyledItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
  1. 在editorEvent函数中的option参数中的option.rect判断出鼠标单击事件发生在哪个单元格,从而找到是哪个窗体小部件。

  1. 自定义一个信号,表示小部件被鼠标单击了。

  1. 在视图类中绑定3步骤中的自定义信号,并调用视图的update函数更新视图。

具体可参考《QAbstractItemView子类如:QTreeView、QTableView等子项单元格复选框勾选/取消勾选功能实现》。

3.2.普通窗体部件、复杂窗体部件绘制的不同说明

上述可以看到普通窗体部件是通过drawControl函数绘制的;而复杂窗体控件是通过drawComplexControl函数绘制的。关于普通窗体部件、复杂窗体部件绘制的不同及这两个函数的含义,请参见《QStyle类用法总结(二)》博文的4.2、4.3节描述。QStyle::CC_Slider、QStyle::CE_CheckBox、QStyle::CE_PushButton各枚举值的含义,请参见《QStyle类用法总结(三)》中的2.2节或Qt Assist。

3.3.可绘制在单元格中的QStyleOption子类

QStyleOption子类如下:

向QAbstractItemView子类如:QTreeView、QTableView等子项单元格插入窗体小部件的功能实现(第2种方法)_第2张图片

虽然说理论上这些子类都可以被绘制在视图的单元格中,但现实中往往由于单元格高度一般不是很高,再加上业务的需求,所以常用的为:QStyleOptionButton(按钮、复选按钮、单选按钮)、QStyleOptionComboBox(组合框), QStyleOptionGroupBox(组框) , QStyleOptionSlider(滑竿条), QStyleOptionSpinBox(上下翻值框), QStyleOptionToolButton(工具按钮)、 QStyleOptionFocusRect(带焦点的矩形)、QStyleOptionProgressBar(进度条)。

你可能感兴趣的:(#,Qt平时遇到的疑难点,QTreeView,QTableView,插入窗体小部件)