【嵌入式——QT】QStyledItemDelegate用法详解

发布时间:2024年01月12日

QStyledItemDelegate是所有Qt item视图的默认委托,在创建它们时就会被安装在它们上面。
QStyledItemDelegate类是模型/视图类之一,是Qt模型/视图框架的一部分。委托允许项目的显示和编辑独立于模型和视图进行开发。
模型中数据项的数据被赋值为ItemDataRole;每个物品可以为每个角色存储一个QVariant。QStyledItemDelegate实现了用户所期望的最常见数据类型的显示和编辑,包括布尔值、整数和字符串。
根据数据在模型中的角色不同,数据将以不同的方式绘制。
继承QStyledItemDelegate需要重写的四个方法:

//返回用于编辑由索引指定的项的小部件
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
//从模型索引指定的数据模型项设置要由编辑器显示和编辑的数据,默认实现将数据存储在editor小部件的user属性中
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
//从editor小部件获取数据,并将其存储在项目索引处的指定模型中,默认实现从editor小部件的user属性获取要存储在数据模型中的值
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
//更新由索引指定的项的编辑器
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override;

首先要继承QStyledItemDelegate,并实现上面提到的四个方法。
代码示例:
CustomStyledItem.h

#include <QObject>
#include <QStyledItemDelegate>
enum COLUMN_HEAD_INDEX {
    CheckBox = 0,
    Id = 1,
    Format=2,
    Type=3,
    SendMode = 4,
    Length=5,
    Data=6,
    SendTimes=7,
    Interval=8,
    Count = 9,
    CANType = 10,
    BRS = 11
};
class CustomStyledItem : public QStyledItemDelegate
{
public:
    explicit CustomStyledItem(QObject* parent, int channel);
    CustomStyledItem();

    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;

private :
    int channel ;
};

CustomStyledItem.cpp

#include "CustomStyledItem.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include "DevCan.h"

CustomStyledItem::CustomStyledItem(QObject* parent, int channel) : QStyledItemDelegate(parent), channel(channel)
{
}

CustomStyledItem::CustomStyledItem()
{
}

QWidget* CustomStyledItem::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    const QString style = R"(QCheckBox {padding-left: 4px; background-color: white; })";
    QCheckBox* cbBox;
    QComboBox* cb;
    switch(index.column()) {
        case CheckBox:
            cbBox = new QCheckBox(parent);
            cbBox->setStyleSheet(style);
            return cbBox;
            break;
        case Id:
            break;
        case Format:
            cb = new QComboBox(parent);
            if(((DevCan::instance().getDevType() == VCI_USBCANFD_II) ||
                (DevCan::instance().getDevType() == VCI_USBCANFD_I)) &&
               (DevCan::instance().getChannelType(channel) == CANFD_CH)) {
                cb->addItem(tr("Data Frame"), 0);
            } else {
                cb->addItem(tr("Data Frame"), 0);
                cb->addItem(tr("Remote Frame"), 1);
            }
            return cb;
            break;
        case Type:
            cb = new QComboBox(parent);
            cb->addItem(tr("Standard Frame"), 0);
            cb->addItem(tr("Extend Frame"), 1);
            return cb;
            break;
        case SendMode:
            cb = new QComboBox(parent);
            cb->addItem(tr("Normal Send"), 0);
            cb->addItem(tr("Single Send"), 1);
            cb->addItem(tr("Send & Receive"), 2);
            cb->addItem(tr("Single Send&Rec"), 3);
            return cb;
            break;
        case Length:
            break;
        case Data:
            break;
        case SendTimes:
            break;
        case Interval:
            break;
        case Count:
            break;
        case CANType:
            break;
        case BRS:
            break;
        default:
            break;
    }
    return NULL;
}

void CustomStyledItem::setEditorData(QWidget* editor, const QModelIndex& index) const
{
    QCheckBox* cbWidget;
    QComboBox* cb;
    int formatIndex;
    switch(index.column()) {
        case CheckBox:
            cbWidget = dynamic_cast<QCheckBox*>(editor);
            cbWidget->setChecked(index.data(Qt::DisplayRole).toString() == "Yes");
            break;
        case Id:
            break;
        case Format:
            cb = dynamic_cast<QComboBox*>(editor);
            cb->setCurrentIndex(index.data(Qt::DisplayRole).toBool()?0:1);
            break;
        case Type:
            cb = dynamic_cast<QComboBox*>(editor);
            cb->setCurrentIndex(index.data(Qt::DisplayRole).toBool()?0:1);
            break;
        case SendMode:
            cb = dynamic_cast<QComboBox*>(editor);
            cb->setCurrentIndex(index.data(Qt::DisplayRole).toBool()?0:1);
            break;
        case Length:
            break;
        case Data:
            break;
        case SendTimes:
            break;
        case Interval:
            break;
        case Count:
            break;
        case CANType:
            break;
        case BRS:
            break;
        default:
            break;
    }
}

void CustomStyledItem::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    QComboBox* cb;
    bool state;
    switch(index.column()) {
        case CheckBox:
            state = dynamic_cast<QCheckBox*>(editor)->isChecked();
            //阻止模型发出信号
            model->blockSignals(true);
            model->setData(index, state ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
            //模型可以发出信号
            model->blockSignals(false);
            model->setData(index, state, Qt::UserRole);
            break;
        case Id:
            break;
        case Format:
            cb = dynamic_cast<QComboBox*>(editor);
            model->setData(index, cb->currentText(), Qt::EditRole);
            break;
        case Type:
            cb = dynamic_cast<QComboBox*>(editor);
            model->setData(index, cb->currentText(), Qt::EditRole);
            break;
        case SendMode:
            cb = dynamic_cast<QComboBox*>(editor);
            model->setData(index, cb->currentText(), Qt::EditRole);
            break;
        case Length:
            break;
        case Data:
            break;
        case SendTimes:
            break;
        case Interval:
            break;
        case Count:
            break;
        case CANType:
            break;
        case BRS:
            break;
        default:
            break;
    }
}

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

那么我们如何使用这个类呢,废话不多说,直接上代码,相信大家一看就全都明白了。
首先呢,我们将表格进行初始化,这是在你的项目代码中完成的,如MainWindow.cpp


void BottomTableView::initUI()
{
	//这个就是关键方法了,这行代码的意思是,将第1列添加一个自定义的小部件,就是我们上面创建的复选框
    ui->tableViewBottom->setItemDelegateForColumn(0, new CustomStyledItem(ui->tableViewBottom, channel));
    //第3列
    ui->tableViewBottom->setItemDelegateForColumn(2, new CustomStyledItem(ui->tableViewBottom, channel));
    //第4列
    ui->tableViewBottom->setItemDelegateForColumn(3, new CustomStyledItem(ui->tableViewBottom, channel));
    //第5列
    ui->tableViewBottom->setItemDelegateForColumn(4, new CustomStyledItem(ui->tableViewBottom, channel));
    modelBottom = new QStandardItemModel(ui->tableViewBottom);
    if(((DevCan::instance().getDevType() == VCI_USBCANFD_II) ||
        (DevCan::instance().getDevType() == VCI_USBCANFD_I)) &&
       (DevCan::instance().getChannelType(channel) == CANFD_CH)) {
        modelBottom->setHorizontalHeaderLabels(QStringList()<<tr(" ")<<tr("Id")<<tr("Format")<<tr("Type")<<tr("SendMode")
                                               <<tr("Length")<<tr("Data(HEX)")<<tr("Send Times")<<tr("Interval(ms)")
                                               <<tr("Count")<<tr("CANType")<<tr("BRS"));
    } else {
        modelBottom->setHorizontalHeaderLabels(QStringList()<<tr(" ")<<tr("Id")<<tr("Format")<<tr("Type")
                                               <<tr("SendMode")<<tr("Length")<<tr("Data(HEX)")<<tr("Send Times")
                                               <<tr("Interval(ms)")<<tr("Count"));
    }
    ui->tableViewBottom->setModel(modelBottom);
    // //隐藏垂直表头
    ui->tableViewBottom->verticalHeader()->setVisible(false);
    //设置表格的网格线风格为自定义虚线风格
    ui->tableViewBottom->setGridStyle(Qt::CustomDashLine);
    //开启交替行颜色
    // ui->tableViewBottom->setAlternatingRowColors(true);
    //设置选择模式为扩展选择,这意味着用户可以选择多个连续或不连续的行和列
    // ui->tableViewBottom->setSelectionMode(QAbstractItemView::ExtendedSelection);
    //设置选择行为为选择行,这意味着当用户点击表格的任何地方时,整行都会被选中
    // ui->tableViewBottom->setSelectionBehavior(QAbstractItemView::SelectRows);
    //设置编辑触发器为无,这意味着表格不应在用户进行任何操作时进入编辑模式
    // ui->tableViewBottom->setEditTriggers(QAbstractItemView::NoEditTriggers);
    //设置表格的框架形状为矩形框
    // ui->tableViewBottom->setFrameShape(QFrame::Box);
    //设置表格框架的阴影为下沉样式,这通常会使表格看起来更立体
    // ui->tableViewBottom->setFrameShadow(QFrame::Sunken);
    //设置列宽
    ui->tableViewBottom->horizontalHeader()->resizeSection(0, 50);
    ui->tableViewBottom->horizontalHeader()->resizeSection(1, 100);
    ui->tableViewBottom->horizontalHeader()->resizeSection(2, 150);
    ui->tableViewBottom->horizontalHeader()->resizeSection(3, 150);
    ui->tableViewBottom->horizontalHeader()->resizeSection(4, 200);
    ui->tableViewBottom->horizontalHeader()->resizeSection(5, 80);
    ui->tableViewBottom->horizontalHeader()->resizeSection(6, 150);
    ui->tableViewBottom->horizontalHeader()->resizeSection(7, 150);
    ui->tableViewBottom->horizontalHeader()->resizeSection(8, 150);
    ui->tableViewBottom->horizontalHeader()->resizeSection(9, 150);
    if(((DevCan::instance().getDevType() == VCI_USBCANFD_II) ||
        (DevCan::instance().getDevType() == VCI_USBCANFD_I)) &&
       (DevCan::instance().getChannelType(channel) == CANFD_CH)) {
        ui->tableViewBottom->horizontalHeader()->resizeSection(10, 100);
        ui->tableViewBottom->horizontalHeader()->resizeSection(11, 100);
    }
    //将表格的水平表头的最后一个section设置为可拉伸
    ui->tableViewBottom->horizontalHeader()->setStretchLastSection(true);
    //取消水平表头的高亮
    ui->tableViewBottom->horizontalHeader()->setHighlightSections(false);
    //水平表头对其方式
    ui->tableViewBottom->horizontalHeader()->setDefaultAlignment(Qt::AlignVCenter);
    //隐藏垂直表头
    // ui->tableViewBottom->verticalHeader()->setVisible(false);
    //垂直表头对其方式居中对齐
    ui->tableViewBottom->verticalHeader()->setDefaultAlignment(Qt::AlignCenter);
    //设置行高
    ui->tableViewBottom->verticalHeader()->setDefaultSectionSize(30);
    //垂直表头的大小调整为适应其内容
    ui->tableViewBottom->verticalHeader()->resizeSections(QHeaderView::ResizeToContents);
}

表格初始化好了,接下来就是像表格上添加数据了,请看下面代码
这里,我用到了QSettings 来进行数据的存储,并取用,这部分知识在之后的文章中会再次提到的。


void BottomTableView::loadSendMsg()
{
    //先清空
    modelBottom->removeRows(0, modelBottom->rowCount());
    int size = 0;
    int colNum ;
    QString applicationName = QString("%1_%2").arg("BASESEND", QString::number(channel));
    QSettings settings(QSettings::NativeFormat, QSettings::UserScope, "Tegcan", applicationName);
    if(((DevCan::instance().getDevType() == VCI_USBCANFD_II) ||
        (DevCan::instance().getDevType() == VCI_USBCANFD_I)) &&
       (DevCan::instance().getChannelType(channel) == CANFD_CH)) {
        size = settings.beginReadArray("sendMsgsFD");
        colNum = 12;
    } else {
        size = settings.beginReadArray("sendMsgs");
        colNum = 10;
    }
    if(size == 0) {
        return;
    }
    QString idStr="";
    QString externFlag = "";
    QString remoteFlag = "";
    QString sendModeStr = "";
    QString canTypeStr = "";
    QString brsStr = "";
    for(int row=0; row<size; row++) {
        settings.setArrayIndex(row);
        for(int col = 0; col< colNum; col++) {
        	//这就相当于是创建了一个单元,然后向单元中填充数据
            QStandardItem* newItem=new QStandardItem;
            int ID = settings.value("ID").toInt();
            int sendModeI = settings.value("SendMode").toInt();
            if(settings.value("ExternFlag").toInt() == 0) {
                externFlag = tr("Standard Frame");
                idStr = settings.value("ID").toString().sprintf("0x%03X", ID);
            } else {
                externFlag = tr("Extend Frame");
                idStr = settings.value("ID").toString().sprintf("0x%08X", ID);
            }
            switch(col) {
                case CheckBox:
                    //CheckBox
                    //Qt::CheckStateRole 这代码复选框角色
                    newItem->setData(Qt::Unchecked, Qt::CheckStateRole);
                    break;
                case Id:
                    //ID
                    newItem->setData(idStr, Qt::DisplayRole);
                    newItem->setEditable(false);
                    break;
                case Format:
                    //Format
                    if(settings.value("RemoteFlag").toInt() == 0) {
                        remoteFlag = tr("Data Frame");
                    } else {
                        remoteFlag = tr("Remote Frame");
                    }
                    if(((DevCan::instance().getDevType() == VCI_USBCANFD_II) ||
                        (DevCan::instance().getDevType() == VCI_USBCANFD_I)) &&
                       (DevCan::instance().getChannelType(channel) == CANFD_CH)) {
                        newItem->setData(remoteFlag, Qt::DisplayRole);
                    } else {
                        newItem->setData(remoteFlag, Qt::EditRole);
                    }
                    break;
                case Type:
                    //Type
                    //Qt::EditRole 这代表可编辑角色
                    newItem->setData(externFlag, Qt::EditRole);
                    break;
                case SendMode:
                    //SendMode
                    switch(sendModeI) {
                        case 0:
                            sendModeStr=tr("Normal Send");
                            break;
                        case 1:
                            sendModeStr=tr("Single Send");
                            break;
                        case 2:
                            sendModeStr=tr("Send & Receive");
                            break;
                        case 3:
                            sendModeStr=tr("Single Send&Rec");
                            break;
                        default:
                            break;
                    }
                    newItem->setData(sendModeStr, Qt::EditRole);
                    break;
                case Length:
                    //Length
                    //Qt::DisplayRole 这个表示该列数据仅是展示,不可修改
                    newItem->setData(settings.value("DataLen").toString(), Qt::DisplayRole);
                    newItem->setEditable(false);
                    break;
                case Data:
                    //Data(HEX)
                    newItem->setData(settings.value("Data").toString(), Qt::DisplayRole);
                    break;
                case SendTimes:
                    //Send Times
                    newItem->setData(settings.value("Times").toString(), Qt::DisplayRole);
                    break;
                case Interval:
                    //Interval(ms)
                    newItem->setData(settings.value("Interval").toString(), Qt::DisplayRole);
                    break;
                case Count:
                    //Count
                    newItem->setData(0, Qt::DisplayRole);
                    newItem->setEditable(false);
                    break;
                case CANType:
                    //CANType
                    if(settings.value("CANType").toInt() == 0) {
                        canTypeStr = "CAN";
                    } else {
                        canTypeStr = "CANFD";
                    }
                    newItem->setText(canTypeStr);
                    newItem->setData(canTypeStr, Qt::DisplayRole);
                    newItem->setEditable(false);
                    break;
                case BRS:
                    //BRS
                    if(settings.value("BRS").toInt() == 0) {
                        brsStr = "Disable BRS";
                    } else {
                        brsStr = "Enable BRS";
                    }
                    // newItem->setText(brsStr);
                    newItem->setData(brsStr, Qt::DisplayRole);
                    newItem->setEditable(false);
                    break;
                default:
                    break;
            }
            modelBottom->setItem(row, col, newItem);
        }
        idStr.clear();
        externFlag.clear();
        remoteFlag.clear();
        sendModeStr.clear();
        canTypeStr.clear();
        brsStr.clear();
    }
    settings.endArray();
}

我们这里用到了复选框,我的项目中的主要是判断哪一条数据是被选中了的,所以我需要判断一下复选框的状态,被选中的数据会被发送到硬件设备上,代码如下:


void BottomTableView::sendMessage(int flag)
{
    if(flag ==0) {
        UlGlobalSet::instance().channel[channel].nStrategicType = TIMING;
    } else if(flag ==1) {
        UlGlobalSet::instance().channel[channel].nStrategicType = RECYCLE;
    } else {
        UlGlobalSet::instance().channel[channel].nStrategicType = ORDER;
    }
    QModelIndex index = ui->tableViewBottom->currentIndex();
    int rows = index.model()->rowCount();
    taskQueue tObjQ;
    for(int i=0; i<rows; i++) {
        QStandardItem* qs = modelBottom->item(i, 0);
        QStandardItemModel* model = qs->model();
        QModelIndex checkBoxIndex = model->index(i, CheckBox);
        int isCheck = model->data(checkBoxIndex, Qt::CheckStateRole).toInt();
        qDebug()<<"isCheck==="<<isCheck;
        if(isCheck == Qt::Checked) {
            taskObj tObj;
            if(((DevCan::instance().getDevType() == VCI_USBCANFD_II) ||
                (DevCan::instance().getDevType() == VCI_USBCANFD_I)) &&
               (DevCan::instance().getChannelType(channel) == CANFD_CH)) {
                if(analyseFDObj(tObj)) {
                    tObjQ.append(tObj);
                }
            } else {
                if(analyseObj(tObj, i)) {
                    tObjQ.append(tObj);
                }
            }
        }
    }
}

代码写完之后就可以双击在你所设置的单元格中直接编辑了,展示一下页面吧!!!
初始页面
图1
下拉选择修改
图2
文本框编辑
图3

文章来源:https://blog.csdn.net/weixin_39723539/article/details/135463769
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。