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);
}
}
}
}
}
代码写完之后就可以双击在你所设置的单元格中直接编辑了,展示一下页面吧!!!
初始页面
下拉选择修改
文本框编辑