QT自定义代理且实现ComboxBox图标加载(个人理解)

1、需求分析

        在QStandardItemModel + QItemSelectionModel + QTableView组件的基础下实现了一个类似如下的表格:

为了防止人为输入数据导致错误,能否实现下面类似这种输入方式(对每个item项进行控制):

也就是修改生日时候弹出一个日历控件,岗位可以有一个下拉列表来选择,性别一样还带图标,薪资使用spinBox来控制。这就涉及到了自定义代理了。


2、自定义代理的概念

        官方概念:指通过继承QStyledItemDelegateQAbstractItemDelegate类来创建自定义的项渲染和编辑方式。

        个人理解就是:

步骤2:将tableView的item设置为自定义组件,代码一般是:ui->tableView->setItemDelegateForColumn(1, &genderDelegate); 这里的genderDelegate是自定义类的对象,这个类里面会创建一个QComboBox组件的指针。这句代码也很容易理解就是给下标1的列(性别)设置一个代理,代理对象就是我们自定义类创建的一个实例。

步骤1:就是从Model中获取数据,然后用这数据设置自己的代理对象。这个通常改写父类的setEditorData方法实现。

步骤1+2 :就能实现从Model数据传给代理然后,tableView再以代理组件方式进行显示了。

步骤3:用户输入数据了,会改变代理的数据。

步骤4:把代理组件中用户输入的数据传递给Model,这样就能保证Model和TableView进行同步了。这个通常改写setModelData方法实现。

步骤3+4:就能实现从用户数据传给代理,然后代理再传给Model,达到同步数据的效果。

总结就是:代理组件就是一个中间介,其目的是为了控制显示效果,和对数据格式进行控制限定等。


3、一个简单代理组件的代码实现

先看效果:

        

代码如下:

//qsalarydelegate.h (自定义类的头文件)

#ifndef QSALARYDELEGATE_H
#define QSALARYDELEGATE_H
#include <QStyledItemDelegate>

//继承QStyledItemDelegate
class QSalaryDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    QSalaryDelegate(QObject *parent = nullptr);

    QWidget *createEditor(QWidget *parent,
                          const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;

    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor,
                      QAbstractItemModel *model,
                      const QModelIndex &index) const override;

    void updateEditorGeometry(QWidget *editor,
                              const QStyleOptionViewItem &option,
                              const QModelIndex &index) const override;

};

#endif // QSALARYDELEGATE_H


//qsalarydelegate.cpp

#include "qsalarydelegate.h"
#include <QSpinBox>

QSalaryDelegate::QSalaryDelegate(QObject *parent):QStyledItemDelegate(parent)
{

}

QWidget *QSalaryDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //用来创建一个代理组件给薪资一列使用,并返回其指针
    QSpinBox *spinBox = new QSpinBox(parent);
    //给这个组件设置进行一些设置
    spinBox->setMaximum(100000);    //最大值
    spinBox->setMinimum(8000);      //最小值
    spinBox->setSingleStep(100);    //单步长

    return  spinBox;    //返回组件指针

}

void QSalaryDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    //把model的数据读取到组件上,然后再通过组件和tableView的item进行关联显示
    //这里是把model的读取到组件上
    /*
        代码解释:
             1、model()->data(index)返回的是一个QVariant类型,它是Qt中的一个万能容器类,它可以存储多种不同类型的值。
                不同的Model可能需要返回不同类型的数据,QVariant提供了一个统一的返回类型,可以进行类型转换得到数据。
            2、index.model()获取与该索引关联的数据模型。
            3、spinBox可以直接设置int,所以提取到的数据直接转为int。
    */
     int value = index.model()->data(index).toInt();
    /*
        代码解释:
            1、QWidget *editor类型是无法设置组件的数据的,因为editor
            2、static_cast<QSpinBox*>(editor)是将QWidget *类型转为QSpinBox *类型,一般来说是子类转父类,
                这里可以从父类转到子类是因为上面返回的editer指针是指向子类(QSpinBox *)的,所以可以转换。
    */
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    //设置组件值,指针传递所以可以更改对象的值
    spinBox->setValue(value);
}

void QSalaryDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    //这里是把用户输入到spinbox的值设置到model里面去
    //获取spinbox的值,同样还是需要强转一次
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    int value = spinBox->value();
    //设置model的数据
    model->setData(index, value);

}

void QSalaryDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //设置组件的位置
    /*
        将编辑器精确定位到:
                位置:与表格/列表中的当前项相同的位置
                大小:与表格/列表中的当前项相同的大小
    */
    editor->setGeometry(option.rect);
}

//mainwindow.h
QSalaryDelegate salaryDelegate;    //MainWindow类内定义一个QSalaryDelegate对象

//mainwindow.cpp
ui->tableView->setItemDelegateForColumn(4, &salaryDelegate);    //自定义组件对象和视图的item关联

总结:自定义一个QWiget类,它继承QStyledItemDelegate,然后分别在这个类里面实现构建一个组件指针,设置改组件指针格式、把Model的数据取到改组件指针中,把用户输入的数据通过改组件指针来设置Model,最后再设置一下组件的位置大小。这就是一个自定义代理组件的大概框架,然后其他一些复杂的东西,你就可以通过折腾你构建的组件指针来达到你想要的目的

最后再加一句:QTableView默认代理组件是QLineEdit


4、使用QComboBox组件作为代理组件实现下拉框和图标

        上面代理组件简单的原因是没有给QSpinBox设置原始数据和其他图标之类的,但是总的步骤是一样的,只不过是增加了对自定义组件的数据增加了处理。

        先看效果:

代码如下(提示如果有调试不出来的可以先清理所有项目再重新构建项目,再运行):

/********************qgenderdelegate.h文件**********************************/

#ifndef QGENDERDELEGATE_H
#define QGENDERDELEGATE_H
#include <QStyledItemDelegate>
#include <QStandardItemModel>

class QGenderDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    QGenderDelegate(QObject *parent = nullptr);

    QWidget *createEditor(QWidget *parent,
                          const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;

    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor,
                      QAbstractItemModel *model,
                      const QModelIndex &index) const override;

    void updateEditorGeometry(QWidget *editor,
                              const QStyleOptionViewItem &option,
                              const QModelIndex &index) const override;   
    void paint(QPainter *painter,
               const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:



};

#endif // QGENDERDELEGATE_H

/********************qgenderdelegate.cpp文件**********************************/

#include "qgenderdelegate.h"
#include <QComboBox>
#include <QPainter>          // 提供绘图功能(QPainter类)
#include <QStyleOptionViewItem> // 提供绘图选项参数
#include <QPalette>          // 提供颜色调色板
#include <QIcon>             // 提供图标功能
#include <QRect>             // 提供矩形区域定义
#include <QApplication>

QGenderDelegate::QGenderDelegate(QObject *parent):QStyledItemDelegate(parent)
{

}


QWidget *QGenderDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //没有使用的参数,进行说明
    Q_UNUSED(option);
    Q_UNUSED(index);

    //新建一个下拉框组件指针作为editor, 同时对其进行设置, 并返回
    QComboBox *editor = new QComboBox(parent);
    //给这个组件添加相应的item,这样你点击这个下拉框就能看到这两个item了
    editor->addItem(QIcon(":/source/images/boy.png"), "男");
    editor->addItem(QIcon(":/source/images/girl.png"), "女");

    return editor;
}

void QGenderDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
      //从model里面获取数据,再通过editor设置显示到tableView中
      //这里的index是属于model的不是属于代理组件的
      QString value = index.data(Qt::DecorationRole).toString();
      //强行转换,获取下拉列表组件的指针
      QComboBox *genderCombox = static_cast<QComboBox *>(editor);
      //给这个组件设置从model获取的值  
      genderCombox->setCurrentText(value);
}

void QGenderDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    //从代理组件中获取数据设置到model中
    QComboBox *genderCombox = static_cast<QComboBox *>(editor);
    QString selectedText = genderCombox->currentText();
    //保存到model中去
    model->setData(index, selectedText, Qt::EditRole);

}

void QGenderDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(editor);
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}


/*
    用户双击性别单元格

    委托调用createEditor创建QComboBox

    setEditorData被调用,将当前值"男"加载到下拉框

    用户选择"女"

    用户按Enter确认

    setModelData被调用,将"女"保存到模型

    模型发出dataChanged信号

    视图调用paint()更新显示

*/

void QGenderDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    /*
 这里算是一个新重点!!!

        1、paint函数的作用是对自定义控件的渲染
        需要的头文件:
                #include <QPainter>          // 提供绘图功能(QPainter类)
                #include <QStyleOptionViewItem> // 提供绘图选项参数
                #include <QPalette>          // 提供颜色调色板
                #include <QIcon>             // 提供图标功能
                #include <QRect>             // 提供矩形区域定义

        2、paint方法也是继承QStyledItemDelegate的方法。其原理是:用户双击性别单元格,
委托调用createEditor创建QComboBox,setEditorData被调用,将之前添加的"男"、"女"加载到
下拉框,假如用户选择"女"后按Enter确认(或者双击)后,setModelData会被调用,将"女"保存到model,
model发出dataChanged信号,视图收到这个信号后调用paint()更新显示。

        
    */

    //获取model中获取存储的文本
    QString text = index.data(Qt::EditRole).toString();
    //根据text创建icon
    QIcon icon = (text == "男") ? QIcon(":/source/images/boy.png"): QIcon(":/source/images/girl.png");
    //绘制背景
    //检查单元格是否被选中, 如果选中:使用高亮颜色填充背景, 如果未选中:使用基础颜色填充背景
    if(option.state & QStyle::State_Selected){
        painter->fillRect(option.rect, option.palette.highlight());
    }
    else{
        painter->fillRect(option.rect, option.palette.base());
    }

    //绘制图标部分
    //创建一个正方形区域 (高度和宽度相同),在这个区域内绘制图标 (自动处理图标的缩放和居中)
    QRect iconRect = option.rect;
    iconRect.setWidth(iconRect.height());
    icon.paint(painter, iconRect);

    //绘制文本部分
    //文本区域从图标右侧开始,留4像素间距,根据选中状态设置文本颜色 (选中时用高亮文本色)
    //垂直居中绘制文本
    QRect textRect = option.rect;
    textRect.setLeft(iconRect.right() + 8);
    painter->setPen(option.state & QStyle::State_Selected ?
                   option.palette.highlightedText().color() :
                   option.palette.text().color());
    painter->drawText(textRect, Qt::AlignVCenter, text);

}

/*************************mainwindow.h 文件*******************************/

QGenderDelegate genderDelegate;    //给mainwindow类增加一个属性


/*************************mainwindow.cpp 文件*******************************/

ui->tableView->setItemDelegateForColumn(1, &genderDelegate);    //进行关联

总结:这个自定义代理下拉框组件和上面的SpinBox组件构建方式还是一样的,只不过在它的基础上增加了一个对应的图标,这里面关键的部分是这个图标并不会加载到model和下拉组件中去,而是调用了point方法,point方法会根据当前model的值是"男"还是"女"选择相应的图标进行渲染。这上面还有一个小瑕疵,就是因为下拉框默认的顺序是"男"在第一列、"女"在第二列,所以在当前单元格为"女"的时候双击会直接变成"男"给人一种突兀感。这个问题想要修改的可自行探索。


5、使用QDateEdit组件作为代理组件

先看效果:

代码如下:

/************************qdateeditdelegate.h 文件******************************/
#ifndef QDATEEDITDELEGATE_H
#define QDATEEDITDELEGATE_H
#include <QStyledItemDelegate>

class QDateEditDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    QDateEditDelegate(QObject *parent = nullptr);

    QWidget *createEditor(QWidget *parent,
                          const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;

    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor,
                      QAbstractItemModel *model,
                      const QModelIndex &index) const override;

    void updateEditorGeometry(QWidget *editor,
                              const QStyleOptionViewItem &option,
                              const QModelIndex &index) const override;


};

#endif // QDATEEDITDELEGATE_H

/************************qdateeditdelegate.cpp 文件******************************/

#include "qdateeditdelegate.h"
#include <QDateEdit>

QDateEditDelegate::QDateEditDelegate(QObject *parent):QStyledItemDelegate(parent)
{

}

QWidget *QDateEditDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QDateEdit *dateEdit = new QDateEdit(parent);
    //设置日期范围
    dateEdit->setMinimumDate(QDate(1900, 1, 1));
    dateEdit->setMaximumDate(QDate(2029, 8, 5));
    //开启日历控件
    dateEdit->setCalendarPopup(true);
    //给日期编辑组件设置为当前日期,数据来源于model
    //这样表格里面是什么日期,双击修改的时候开始显示的也是这个日期增加用户体验
    QString value  = index.data().toString();
    QDate date = QDate::fromString(value, "yyyy-M-dd");
    dateEdit->setDate(date);

    return  dateEdit;
}

void QDateEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    //从model获取数据传给editor
     QString value  = index.data().toString();
     QDate date = QDate::fromString(value, "yyyy-M-dd");
     QDateEdit *dateEdit = static_cast<QDateEdit *>(editor);
     dateEdit->setDate(date);

}

void QDateEditDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    //从editor获取数据给model
    QDateEdit *dateEdit = static_cast<QDateEdit *>(editor);
    QDate  date = dateEdit->date();
    //因为没有重写point所以model填入怎么格式的数据在tableView中就会怎么显示
    QString value = date.toString("yyyy-M-dd");
    model->setData(index, value);
}

void QDateEditDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    editor->setGeometry(option.rect);
}


/*************************mainwindow.h 文件*******************************/

QDateEditDelegate dateEditDelegate;    //给mainwindow类增加一个属性


/*************************mainwindow.cpp 文件*******************************/

ui->tableView->setItemDelegateForColumn(2, &dateEditDelegate);    //进行关联

以上就是为tableView的item增加一个自定义日历组件的代理了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值