Qt自定义QAbstractItemModel实现文件夹/文件树

Qt自定义QAbstractItemModel实现文件夹/文件树_第1张图片

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

你可能感兴趣的:(Qt,qt,开发语言)