QAbstractItemModel是QAbstractListModel、QAbstractTableModel和QAbstractProxyModel的父类
使用时需实现一下方法:
//表头实现
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
//数据实现
QVariant data(const QModelIndex &index, int role) const override;
//数据编辑
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
//子索引
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
//父索引
QModelIndex parent(const QModelIndex &index) const override;
//行数
int rowCount(const QModelIndex &parent) const override;
//列数
int columnCount(const QModelIndex &parent) const override;
//数据标记
Qt::ItemFlags flags(const QModelIndex &index) const override;
---------------------------------------------------------------------------------------------------------------------------------
但底层仍需一个树形内存结构来进行数据维护,例如:
VVTreeItem包含type、value、父节点、子节点4个必要属性,及几个附加属性
class VVTreeItem
{
public:
enum Type
{
UNKNOWN = -1,
DIR,
FILE,
BEAT,
COMMAND
};
explicit VVTreeItem(VVTreeItem *parent = nullptr);
~VVTreeItem();
void setBasedType(int basedType);
void addChild(VVTreeItem *item);
void insertChild(int index, VVTreeItem *item);
void removeChild(VVTreeItem *item);
void removeChildren();
VVTreeItem *child(int row) { return _children.value(row); }
VVTreeItem *parent() { return _parent; }
int childCount() const { return _children.count(); }
QVariant data(int column) const;
//设置、获取节点存的数据指针
void setPtr(void* p) { _ptr = p; }
void* ptr() const { return _ptr; }
void setRow(int row) { _row = row; } //保存该节点是其父节点的第几个子节点,查询优化所用
int row() const { return _row; } //返回本节点位于父节点下第几个子节点
Type getType() const { return _type; }
void setType(const Type &value) { _type = value; }
private:
QList _children; // 子节点
VVTreeItem* _parent; // 父节点
Type _type; // 此节点保存的数据类型
void* _ptr; // 存储数据的指针
int _row; // 此item位于父节点中第几个
int _basedType;
};
将该VVTreeItem数据结构应用到QAbstractItemModel中:
#ifndef VVTREEMODEL_H
#define VVTREEMODEL_H
#include
#include "vv_tree_item.h"
struct VVBeat;
struct VVCommand;
class VVTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit VVTreeModel(const QStringList& headers, QObject *parent = nullptr);
~VVTreeModel() override;
void setBasedType(int basedType);
VVTreeItem* root(); //获取根节点
VVTreeItem::Type dataType(QModelIndex index); //获取当前index的类型
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool addDir(QString name); //添加文件夹
bool addDir(VVDir* vvDir); //添加文件夹
bool addFile(const QString &name, int executionNumber, const QString &cycleStr, QModelIndex parent);//添加文件
bool addFile(VVFile* vvFile, QModelIndex parent); //添加文件
void addBeat(QModelIndex index, VVBeat *beat); //添加节拍
void addBeatRight(QModelIndex index, int start, VVBeat* beat); //添加节拍(节拍命令树)
void addCommand(QModelIndex index, int beatIndex, VVCommand *command); //添加命令
void addCommandRight(QModelIndex parent, int start, VVCommand *command); //添加命令(节拍命令树)
void pasteBeat(QModelIndex index, int start, VVBeat *beat); //粘贴命令
void pasteCommand(QModelIndex index, int start, VVCommand *command); //粘贴命令
void insertBeat(QModelIndex index, int row, VVBeat* beat); //插入节拍
void insertBeatRight(QModelIndex index, int start, VVBeat* beat); //插入节拍(右侧)
void deleteBeat(QModelIndex index, VVBeat* beat); //删除节拍
void deleteBeatRight(QModelIndex parent, int first, int last); //删除节拍(右侧)
void deleteCommand(QModelIndex index, VVBeat *beat, VVCommand* command); //删除命令
void deleteCommandRight(QModelIndex parent, int first, int last); //删除命令(右侧)
void deleteDirOrFile(QModelIndex index); //删除文件夹or文件
bool modifyDir(const QString &oldDir, const QString &newDir, QModelIndex index); //修改文件夹名称
bool modifyFile(VVFile *oldVVFile, const QString &newFile, const int &newNumOfExe,
const QString &newCycleStr, QModelIndex index, int multiplier = 0); //修改文件名称
void modifyBeat(VVBeat *beat, int index, int type, QString des, QModelIndex beat_index); //修改节拍
void modifyCommand(QModelIndex index, int beatIndex, VVCommand *vvCommand, VVCommand* vvCommandNew);//修改命令
void modifyCommandRight(QModelIndex index, VVCommand *vvCommand, VVCommand* vvCommandNew); //修改命令(右侧)
public:
VVTreeItem *itemFromIndex(const QModelIndex &index) const;
private:
QStringList _headers; //水平表头
VVTreeItem* _rootItem; //根节点
int _basedType; //当前是基于节拍,还是基于时间
};
#endif // VVTREEMODEL_H
另外对于View中的数据,可以自定义展示方式,使用代理
#ifndef RIGHT_DELEGATE_H
#define RIGHT_DELEGATE_H
#include
#include
#include
#include
#include
#include
#include
#include
#include "icd_data.h"
class RightDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
RightDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent) { }
void setItems(const QHash& hash) {
_hash = hash;
_texts = hash.keys();
qSort(_texts);
}
void setICDData(QSharedPointer icdData) { _icdData = icdData; }
public:
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
Q_UNUSED(option)
QString command_type = index.model()->data(index.sibling(index.row(), 2)).toString();
QString text = index.model()->data(index, Qt::DisplayRole).toString();
QComboBox *editor = new QComboBox(parent);
editor->setEditable(true);
QSpinBox* editor_int = new QSpinBox(parent);
QDoubleSpinBox* editor_double = new QDoubleSpinBox(parent);
QLineEdit *editor_txt = new QLineEdit(parent);
switch (index.column()) {
case 3: //! 消息名称
case 4: //! 消息ID
{
if(command_type == "PUSH_INTO_FIFO" || command_type == "CLEAR_FIFO")
return nullptr;
editor->addItems(_texts);
QCompleter *completer = new QCompleter(_texts, editor);
completer->setCaseSensitivity(Qt::CaseInsensitive);
editor->setCompleter(completer);
}
return editor;
case 5: //! 信号名称
{
if(command_type == "PUSH_INTO_FIFO" || command_type == "CLEAR_FIFO")
return nullptr;
QStringList list = _icdData->getSigNamesHash(index.sibling(index.row(), 3).data().toString()).keys(); //!TODO. 这里是根据的ICD_ID,但是会有问题,因为历史问题,这里暂使用ICD_NAME
qSort(list);
editor->addItems(list);
QCompleter *completer = new QCompleter(list, editor);
completer->setCaseSensitivity(Qt::CaseInsensitive);
editor->setCompleter(completer);
}
return editor;
case 6: //! 信号ID
{
if(command_type == "PUSH_INTO_FIFO" || command_type == "CLEAR_FIFO")
return nullptr;
QStringList list = _icdData->getSigIDsHash(index.sibling(index.row(), 3).data().toString()).keys(); //!TODO. 这里是根据的ICD_ID,但是会有问题,因为历史问题,这里暂使用ICD_NAME
qSort(list);
editor->addItems(list);
QCompleter *completer = new QCompleter(list, editor);
completer->setCaseSensitivity(Qt::CaseInsensitive);
editor->setCompleter(completer);
}
return editor;
case 9: //! 值
if(index.sibling(index.row(), 2).data(0).toString() == "COMBINATION" ||
index.sibling(index.row(), 2).data(0).toString() == "RESET" ||
index.sibling(index.row(), 2).data(0).toString() == "SVPC_CHECK" ||
index.sibling(index.row(), 2).data(0).toString() == "PUSH_INTO_FIFO" ||
index.sibling(index.row(), 2).data(0).toString() == "CLEAR_FIFO")
return nullptr;
case 10:
return nullptr;//nisz add 2022-6-17
if(isNum(text.toStdString()))
{
if(text.contains("."))
return editor_double;
else
return editor_int;
}
return editor_txt;
}
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
QString text = index.model()->data(index, Qt::DisplayRole).toString();
QComboBox* comboBox = dynamic_cast(editor);
QSpinBox* spinBox = dynamic_cast(editor);
QDoubleSpinBox* doubleSpinBox = dynamic_cast(editor);
QLineEdit* lineEdit = dynamic_cast(editor);
if(comboBox)
{
int tindex = comboBox->findText(text);
comboBox->setCurrentIndex(tindex);
}
else
{
if(isNum(text.toStdString()))
{
if(spinBox)
{
spinBox->setValue(text.toInt());
}
else
{
doubleSpinBox->setValue(text.toDouble());
}
}
else if(lineEdit)
lineEdit->setText(text);
}
}
void setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const override
{
QString beforeEditValue = model->data(index).toString();
QString currentValue = "";
bool changeOtherColumnFlag = true;
QComboBox* comboBox = dynamic_cast(editor);
QSpinBox* spinBox = dynamic_cast(editor);
QDoubleSpinBox* doubleSpinBox = dynamic_cast(editor);
QLineEdit* lineEdit = dynamic_cast(editor);
if(comboBox)
{
QString lineEditStr = comboBox->lineEdit()->text();
qDebug() << "lineEditStr: " << lineEditStr << endl;
currentValue = comboBox->currentText();
qDebug() << "comboBox: " << currentValue << endl;
if(currentValue == beforeEditValue)
{
changeOtherColumnFlag = false;
}
if(!currentValue.isEmpty())
model->setData(index, currentValue, Qt::EditRole);
}
else if(spinBox)
{
currentValue = QString::number(spinBox->value());
model->setData(index, currentValue, Qt::EditRole);
}
else if(doubleSpinBox)
{
currentValue = QString::number(doubleSpinBox->value());
model->setData(index, currentValue, Qt::EditRole);
}
else if(lineEdit)
{
currentValue = lineEdit->text();
model->setData(index, currentValue, Qt::EditRole);
}
if(currentValue.isEmpty())
return;
//不同的列对应不同的操作
if(changeOtherColumnFlag)
{
if(index.column() == 3)
{
// 有对应的msgID就直接设置上
model->setData(index.sibling(index.row(), 4), _hash.value(currentValue), Qt::EditRole);
QHash sigNamesHash = _icdData->getSigNamesHash(_hash.value(currentValue));
if(!sigNamesHash.contains(model->data(index.sibling(index.row(), 5)).toString()))
{
model->setData(index.sibling(index.row(), 5), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 6), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 7), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 8), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 9), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 10), "", Qt::EditRole);
}
}
else if(index.column() == 4)
{ // 有对应的msgName就直接设置上
model->setData(index.sibling(index.row(), 3), _hash.value(currentValue), Qt::EditRole);
QHash sigIDsHash = _icdData->getSigIDsHash(_hash.value(currentValue));
if(!sigIDsHash.contains(model->data(index.sibling(index.row(), 6)).toString()))
{
model->setData(index.sibling(index.row(), 5), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 6), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 7), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 8), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 9), "", Qt::EditRole);
model->setData(index.sibling(index.row(), 10), "", Qt::EditRole);
}
}
else if(index.column() == 5)
{ // 有对应的sigID就直接设置上
QHash hash = _icdData->getSigNamesHash(model->data(index.sibling(index.row(), 3)).toString());
QStringList infos = hash.value(currentValue).split(",");
model->setData(index.sibling(index.row(), 6), infos.at(0), Qt::EditRole);
model->setData(index.sibling(index.row(), 7), infos.at(1), Qt::EditRole);
model->setData(index.sibling(index.row(), 8), infos.at(2), Qt::EditRole);
}
else if(index.column() == 6)
{ // 有对应的sigName就直接设置上
QHash hash = _icdData->getSigIDsHash(model->data(index.sibling(index.row(), 3)).toString());
QStringList infos = hash.value(currentValue).split(",");
model->setData(index.sibling(index.row(), 5), infos.at(0), Qt::EditRole);
model->setData(index.sibling(index.row(), 7), infos.at(1), Qt::EditRole);
model->setData(index.sibling(index.row(), 8), infos.at(2), Qt::EditRole);
}
}
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
Q_UNUSED(index)
editor->setGeometry(option.rect);
}
private:
bool isNum(std::string str) const
{
std::stringstream sin(str);
double d;
char c;
if(!(sin >> d))
{
return false;
}
if (sin >> c)
{
return false;
}
return true;
}
private:
QHash _hash; //
QStringList _texts; //
QString _msgID; //当前已经选中的
QSharedPointer _icdData; //
};
#endif // RIGHT_DELEGATE_H