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

目录

1.前言

2.实现详解

3.附加说明

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

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

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


1.前言

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

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

  • 模型类从 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 <QStyleOptionButton> 
#include<QApplication>
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);
}

效果如下:

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子类如下:

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

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 当您在QTableView中单击单元格时,单元格将成为当前活动单元格,并且焦点将设置为该单元格。如果您再次单击同一单元格,表格将捕获双击事件,并尝试将单元格编辑器打开以允许您编辑单元格的内容。但是,由于您已经使用`QAbstractItemView::NoEditTriggers`禁用了编辑功能,因此表格将自动将焦点移动到最后一个单元格。 如果您不希望表格在双击单元格时自动将焦点移动到最后一个单元格,可以重新实现鼠标双击事件并忽略它。例如,您可以在派生的QTableView类中添加以下代码: ``` void MyTableView::mouseDoubleClickEvent(QMouseEvent *event) { event->ignore(); } ``` 这将忽略鼠标双击事件,从而防止表格将焦点移动到最后一个单元格。 ### 回答2: 在QT的QTableView中,当设置QAbstractItemView::NoEditTriggers时,表格的焦点即使是通过点击单元格获得的,也不应当自动移动到最后一个单元格。因此,如果点击两次单元格后表格的焦点确实发生了移动,那么可以考虑以下几点原因: 1. 代码逻辑问题:可能在单元格点击事件的处理函数中,有相关的代码逻辑会将焦点设置到最后一个单元格。可以检查相关的函数或者槽函数,验证是否存在这样的代码。 2. QTableView的其他设置:除了设置QAbstractItemView::NoEditTriggers之外,还需要检查是否有其他的设置导致了焦点的移动。例如,如果设置了QAbstractItemView::ScrollPerItem触发的滚动选项,可能会导致焦点移动到最后一个可见的单元格。 3. 模型索引问题:可能是在处理单元格点击事件时,没有正确处理模型索引的指向。可以确认单元格点击事件槽函数中是否有对模型索引的处理,以及确保焦点确实被设置到了点击的单元格。 总之,要解决这个问题,需要逐一排查上述可能的原因,并对其进行验证。 ### 回答3: 在QT的QTableView中,当点击两次单元格后,焦点自动移动到最后一个单元格的原因可能是由于以下几个可能的问题: 1. QTableView的selectionBehavior属性被设置为SelectRows或SelectColumns:如果selectionBehavior属性被设置为SelectRows或SelectColumns,则当双击一个单元格时,整行或整列将被选中,而不是仅仅选中单元格。这可能会导致焦点自动移动到最后一个单元格。 解决方法:将selectionBehavior属性设置为SelectItems,这将只选中单元格,而不是整行或整列。 2. QAbstractItemView的editTriggers属性设置不正确:虽然您已经设置了QAbstractItemView::NoEditTriggers,但是可能在其他地方设置了其他的编辑触发器。在这情况下,双击单元格会触发默认的编辑行为,导致焦点自动移动。 解决方法:在设置editTriggers属性时,确保将其他编辑触发器设置为None,以确保禁用任何编辑行为,例如QAbstractItemView::NoEditTriggers | QAbstractItemView::NoEditTriggers(如果有其他编辑触发器)。 3. 代码中的其他逻辑错误:检查其他可能会影响焦点移动的代码。例如,在模型或单元格的选中信号槽中可能包含了有关焦点的相关逻辑。 解决方法:检查代码中的其他信号槽或条件语句,确保没有其他逻辑错误或条件会导致焦点自动移动。 通过检查以上可能的问题,您应该能够找到引起焦点自动移动到最后一个单元格的原因,并相应地进行修复。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值