QT自定义树形结构,自定义model、自定义Delegate代理

时间:2019-11-27
本文章向大家介绍QT自定义树形结构,自定义model、自定义Delegate代理,主要包括QT自定义树形结构,自定义model、自定义Delegate代理使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

树形结构体
class YjProjectModelData
{
public:
    explicit YjProjectModelData(YjProjectModelData *parentItem = 0){m_parentItem = parentItem;}
    ~YjProjectModelData(){}

    YjProjectModelData *child(int row){ return children.at(row);}
    int childCount() const{ return children.count();}
    QVariant data(int column) const;
    int row() const{
        if (m_parentItem)
            return m_parentItem->children.indexOf(const_cast<YjProjectModelData*>(this));
        return 0;
    }
    YjProjectModelData *parentItem(){
        return m_parentItem;
    }
public:
    qint64 dataId;
    QString dataName;
    QString labelIds;
    qint64 parentId;
    QVector<YjProjectModelData*> children;
    QString cTime;
    QString lastUpdateTime;
    QVector<YjProjectLabel>labelList;
    Qt::CheckState checked = Qt::Unchecked;
    YjProjectModelData &operator=(const YjProjectModelData &data)
    {
        this->dataId = data.dataId;
        this->dataName = data.dataName;
        this->labelIds = data.labelIds;
        this->parentId = data.parentId;
        this->children = data.children;
        this->cTime = data.cTime;
        this->lastUpdateTime = data.lastUpdateTime;
        this->labelList = data.labelList;
        return *this;
    }
private:
    YjProjectModelData *m_parentItem;
};

树形model.h:
#ifndef YJPROJECTTREEMODEL_H
#define YJPROJECTTREEMODEL_H

#include <QAbstractItemModel>
#include <QIcon>
#include "YjTreeItem.h"
#include "YjModelProject.h"

enum ColumnIndex{
    Name,
    category,
    createDate,
    lastModifyDate
};

class YjProjectTreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    YjProjectTreeModel(QObject *parent, YjProjectModelData* rootItem);
    ~YjProjectTreeModel();
    QVariant data(const QModelIndex &index, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const override;
    QModelIndex index(int row, int column,
                      const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role);
    YjProjectModelData *rootItem() const;

public slots:
    void onUpdataTree();
private:
    void setAllchildandParentcheackState(YjProjectModelData *currentItem);          //设置关联的父子选中状态级联改变
    /////////////////////////////////////////////////////////////////////////
    /// 函数名称:
    /// 函数功能:递归设置所有的子项目为全选或全不选状态item
    /// 返回值:
    /// 函数参数:item 当前项目 check true时为全选,false时全不选
    /////////////////////////////////////////////////////////////////////////
    void treeItem_checkAllChild(YjProjectModelData * item, bool check);
    void treeItem_checkAllChild_recursion(YjProjectModelData * item,bool check);

    ////////////////////////////////////////////////////////////////////////
    /// 函数名称: treeItem_CheckChildChanged
    /// 函数功能: 根据子节点的改变,更改父节点的选择情况
    ////////////////////////////////////////////////////////////////////////
    void treeItem_CheckChildChanged(YjProjectModelData * item);

    ////////////////////////////////////////////////////////////////////////
    /// 函数功能: 测量兄弟节点的情况,如果都选中返回Qt::Checked,都不选中Qt::Unchecked,不完全选中返回Qt::PartiallyChecked
    /// 返回值: 如果都选中返回Qt::Checked,都不选中Qt::Unchecked,不完全选中返回Qt::PartiallyChecked
    ////////////////////////////////////////////////////////////////////////
    Qt::CheckState checkSibling(YjProjectModelData * item);


    YjProjectModelData* m_rootItem;
    QIcon m_icon;
};

#endif // YJPROJECTTREEMODEL_H

model.cpp


#include "YjProjectTreeModel.h" 
#include <QDebug>

const int columnNum = 4;


YjProjectTreeModel::YjProjectTreeModel(QObject *parent, YjProjectModelData *rootItem):QAbstractItemModel(parent)
{
    m_rootItem = rootItem;
    m_icon = QIcon(":/YjHealthePlus/image/dir.png");
}

YjProjectTreeModel::~YjProjectTreeModel()
{
    if(m_rootItem != NULL){
        delete m_rootItem;
    }
}

QVariant YjProjectTreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    //    if (role != Qt::DisplayRole)
    //        return QVariant();

    YjProjectModelData *item = static_cast<YjProjectModelData*>(index.internalPointer());

    if(role == Qt::UserRole +1){
        return item->dataName;
    }
    if(role == Qt::UserRole +11){
        return item->checked;
    }
    if(role == Qt::DisplayRole && index.column() == category){
        QString m_category = "";
        for(auto obj:item->labelList)
        {
            m_category += obj.m_labelName;
            m_category += ",";
        }
        if(m_category == ""){
            return QStringLiteral("未指定诊断类别");
        }else{
            m_category.chop(1);
            return m_category;
        }
    }
    if(role == Qt::DisplayRole && index.column() == createDate){
        return item->cTime;
    }
    if( role == Qt::DisplayRole && index.column() == lastModifyDate){
        return item->lastUpdateTime;
    }
    return QVariant();
}

Qt::ItemFlags YjProjectTreeModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags theFlags = QAbstractItemModel::flags(index);
    if (index.isValid()) {
        theFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled;
        if (index.column() == Name)
            theFlags |= Qt::ItemIsUserCheckable;
    }
    return theFlags;
}

QVariant YjProjectTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
        if (section == Name)
            return QStringLiteral("数据名称");
        else if (section == category)
            return QStringLiteral("指定诊断类别");
        else if (section == createDate)
            return QStringLiteral("创建日期");
        else if (section == lastModifyDate)
            return QStringLiteral("最后操作日期");
    }
    return QVariant();
}

QModelIndex YjProjectTreeModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();
    YjProjectModelData *parentItem;

    if (!parent.isValid())
        parentItem = m_rootItem;
    else
        parentItem = static_cast<YjProjectModelData*>(parent.internalPointer());

    YjProjectModelData *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();

}

QModelIndex YjProjectTreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    YjProjectModelData *childItem = static_cast<YjProjectModelData*>(index.internalPointer());
    YjProjectModelData *parentItem = childItem->parentItem();

    if (parentItem == m_rootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int YjProjectTreeModel::rowCount(const QModelIndex &parent) const
{
    YjProjectModelData *parentItem;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = m_rootItem;
    else
        parentItem = static_cast<YjProjectModelData*>(parent.internalPointer());

    return parentItem->childCount();
}

int YjProjectTreeModel::columnCount(const QModelIndex &parent) const
{
    return columnNum;
}

bool YjProjectTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(role == Qt::CheckStateRole){
        YjProjectModelData *item = static_cast<YjProjectModelData*>(index.internalPointer());
        //        item->checked = item->checked;
        if(item->checked == Qt::Checked)
        {
            item->checked = Qt::Unchecked;
        }else{
            item->checked = Qt::Checked;
        }
        setAllchildandParentcheackState(item);
        qDebug()<<"TreeitemState:"<<item->checked<<" item:" <<item->dataName;
        onUpdataTree();
        return true;
    }
    return QAbstractItemModel::setData(index,value,role);
}

void YjProjectTreeModel::onUpdataTree()
{
    layoutAboutToBeChanged();
    //    inspectChecked();
    layoutChanged();
}

void YjProjectTreeModel::setAllchildandParentcheackState(YjProjectModelData *currentItem)
{
    if ( currentItem == nullptr )
        return ;

        Qt::CheckState state = currentItem -> checked; //获取当前的选择状态
        treeItem_CheckChildChanged ( currentItem );
        if ( state != Qt :: PartiallyChecked )
        {
            //当前是选中状态,需要对其子项目进行全选
            treeItem_checkAllChild ( currentItem , state == Qt :: Checked ? true : false );
        }
}

void YjProjectTreeModel::treeItem_checkAllChild(YjProjectModelData *item, bool check)
{
    if(item == nullptr)
        return;
    int rowCount = item->childCount();
    for(int i=0;i<rowCount;++i)
    {
        YjProjectModelData* childItems = item->child(i);
        treeItem_checkAllChild_recursion(childItems,check);
    }
    item->checked = (check ? Qt::Checked : Qt::Unchecked);
}

void YjProjectTreeModel::treeItem_checkAllChild_recursion(YjProjectModelData *item, bool check)
{
    if(item == nullptr)
        return;
    int rowCount = item->childCount();
    for(int i=0;i<rowCount;++i)
    {
        YjProjectModelData* childItems = item->child(i);
        treeItem_checkAllChild_recursion(childItems,check);
    }
    item->checked = (check ? Qt::Checked : Qt::Unchecked);

}

void YjProjectTreeModel::treeItem_CheckChildChanged(YjProjectModelData *item)
{
    if(nullptr == item)
        return;
    Qt::CheckState siblingState = checkSibling(item);
    qDebug()<<siblingState;
    YjProjectModelData * parentItem = item->parentItem();
    if(nullptr == parentItem)
        return;
    if(Qt::PartiallyChecked == siblingState)
    {
        parentItem->checked = Qt::PartiallyChecked;
    }
    else if(Qt::Checked == siblingState)
    {
        parentItem->checked = Qt::Checked;
    }
    else
    {
        parentItem->checked = Qt::Unchecked;
    }
    treeItem_CheckChildChanged(parentItem);
}

Qt::CheckState YjProjectTreeModel::checkSibling(YjProjectModelData *item)
{
    YjProjectModelData *parent = item->parentItem();
    if(nullptr == parent)
        return (Qt::CheckState) item->checked;
    int brotherCount = parent->childCount();
    int checkedCount(0),unCheckedCount(0);
    Qt::CheckState state;
    for(int i=0;i<brotherCount;++i){
        YjProjectModelData* siblingItem = parent->child(i);
        state = (Qt::CheckState)siblingItem->checked;
        if(Qt::PartiallyChecked == state)
            return Qt::PartiallyChecked;
        else if(Qt::Unchecked == state)
            ++unCheckedCount;
        else
            ++checkedCount;
        if(checkedCount>0 && unCheckedCount>0)
            return Qt::PartiallyChecked;
    }
    if(unCheckedCount>0)
        return Qt::Unchecked;
    return Qt::Checked;
}

YjProjectModelData *YjProjectTreeModel::rootItem() const
{
    return m_rootItem;
}


代理类:
YjAddStructItemDelegate.h

#ifndef YJADDSTRUCTITEMDELEGATE_H
#define YJADDSTRUCTITEMDELEGATE_H

#include <QStyledItemDelegate>
#include <QPushButton>
#include <QTableView>
#include <QIcon>


class YjTableView;
class YjStructParaSelectitem;


class YjAddStructItemDelegate: public QStyledItemDelegate
{
    Q_OBJECT
public:
    YjAddStructItemDelegate(QObject *parent);
    ~YjAddStructItemDelegate();

    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    void setEditorData(QWidget *editor,const QModelIndex &index);
    void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const Q_DECL_OVERRIDE;
    void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
    void setSelectRowClear();

    YjTableView* m_tableView;

signals:
    void deleteData(const QModelIndex& index);

private:
    QPoint m_mousePoint;  // 鼠标位置
    QScopedPointer<QPushButton> m_pButton;
    int m_nSpacing;  // 按钮之间的间距
    int m_nWidth;  // 按钮宽度
    int m_nHeight;  // 按钮高度
    int m_nType;  // 按钮状态-1:划过 2:按下
    int m_moveRow;
    int m_pressedRow;
    QIcon m_deleteICon;
};

#endif // YJADDSTRUCTITEMDELEGATE_H



#include <QPainter>
#include <QEvent>
#include <QMouseEvent>
#include <QLineEdit>
#include "YjAddStructItemDelegate.h"
#include "yjaddstructitemmodel.h"
#include "YjModelProject.h"
#include "YjTipConverDialog.h"

#include "YjTableView.h"
#include <QDebug>

YjAddStructItemDelegate::YjAddStructItemDelegate(QObject *parent): QStyledItemDelegate(parent),
    m_pButton(new QPushButton()),
    m_nSpacing(5),
    m_nWidth(30),
    m_nHeight(30)
{
    m_moveRow = -1;
    m_pressedRow = -1;
    m_pButton->setStyleSheet("QPushButton {border: none; "> transparent; color:rgb(245,87,62);} \
                             QPushButton:hover {color:rgb(0, 255, 0);} \
                             QPushButton:pressed {color:rgb(0, 255, 0);}");

                             m_deleteICon = QIcon("./data/image/delete.png");
}

YjAddStructItemDelegate::~YjAddStructItemDelegate()
{

}

QWidget *YjAddStructItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.column() == 0){
        QLineEdit *editor = new QLineEdit(parent);
        editor->setPlaceholderText(QStringLiteral("请输入数字"));
        QRegExp regExp("[0-9]{0,10}");
        editor->setValidator(new QRegExpValidator(regExp, parent));
        return editor;
    }
    if(index.column() == 1)
    {
        QLineEdit *editor = new QLineEdit(parent);
        editor->setPlaceholderText(QStringLiteral("请输入"));
        return editor;
    }
    if(index.column() == 2)
    {
        return QStyledItemDelegate::createEditor(parent,option,index);
    }
}

void YjAddStructItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index)
{
    if(index.column() != 2)
    {
        QString text = index.model()->data(index, Qt::EditRole).toString();
        QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
        lineEdit->setText(text);
    }else{
        return QStyledItemDelegate::setEditorData(editor,index);
    }
}

void YjAddStructItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
    QString text = lineEdit->text();
    model->setData(index, text, Qt::EditRole);
}

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

void YjAddStructItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItem viewOption(option);
    initStyleOption(&viewOption, index);
    if (option.state.testFlag(QStyle::State_HasFocus))
        viewOption.state = viewOption.state ^ QStyle::State_HasFocus;

    bool l_flag = false;
    const QAbstractItemModel * model = index.model();

    if (index.column() == 0)
    {
        QString l_paralValue = model->data(index, Qt::UserRole + 1).toString();
        //绘制文本
        painter->drawText(option.rect, Qt::AlignHCenter | Qt::AlignVCenter, l_paralValue);
    }
    else if (index.column() == 1)
    {
        QString l_paralName = model->data(index, Qt::UserRole + 2).toString();
        //绘制文本
        painter->drawText(option.rect, Qt::AlignHCenter | Qt::AlignVCenter, l_paralName);
    }
    else if (index.column() == 2)
    {
        //        const QAbstractItemModel * model = index.model();
        //	int l_labelType = model->data(index, Qt::UserRole+1).toInt();
        int nCount = 1;
        int nHalf = (option.rect.width() - m_nWidth * nCount - m_nSpacing * (nCount - 1)) / 2;
        int nTop = (option.rect.height() - m_nHeight) / 2;

        QStyleOptionButton delBtn;
        delBtn.icon = m_deleteICon;
        delBtn.iconSize = QSize(28, 28);

        delBtn.rect = QRect(option.rect.left() + nHalf + m_nWidth * 0 + m_nSpacing * 0,
                            option.rect.top() + nTop, m_nWidth, m_nHeight);
        delBtn.state |= QStyle::State_Enabled;

        if (delBtn.rect.contains(m_mousePoint))
        {
            if (m_nType == 0)
            {
                delBtn.state |= QStyle::State_MouseOver;
            }
            else if (m_nType == 1)
            {
                delBtn.state |= QStyle::State_Sunken;
            }
        }
        m_pButton->style()->drawControl(QStyle::CE_PushButton, &delBtn, painter, m_pButton.data());
    }
}

bool YjAddStructItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    m_nType = -1;
    m_moveRow = -1;
    m_pressedRow = -1;
    bool bRepaint = false;

    QMouseEvent *pEvent = static_cast<QMouseEvent *> (event);
    m_mousePoint = pEvent->pos();

    bool l_bPressed = false;
    if (event->type() == QEvent::MouseMove)
    {
        m_moveRow = index.row();
    }
    else if (QEvent::MouseButtonPress)
    {
        m_moveRow = index.row();
        m_pressedRow = index.row();
    }
    if (index.column() == 2)
    {
        const QAbstractItemModel * model = index.model();

        int nCount = 1;
        int nHalf = (option.rect.width() - m_nWidth * nCount - m_nSpacing * (nCount - 1)) / 2;
        int nTop = (option.rect.height() - m_nHeight) / 2;
        QRect l_rect = QRect(option.rect.left() + nHalf + m_nWidth * 0 + m_nSpacing * 0,
                             option.rect.top() + nTop, m_nWidth, m_nHeight);
        bRepaint = true;
        if (l_rect.contains(m_mousePoint))
        {
            bRepaint = true;
            switch (event->type())
            {
            // 鼠标滑过
            case QEvent::MouseMove:
            {
                m_nType = 0;
                break;
            }
                // 鼠标按下
            case QEvent::MouseButtonPress:
            {
                m_nType = 1;
                break;
            }
                // 鼠标释放
            case QEvent::MouseButtonRelease:
            {
                YjTipConverDialog l_tipDialog;
                l_tipDialog.setTipMes(QStringLiteral("是否确认删除!"));
                l_tipDialog.exec();
                if (!l_tipDialog.getIsConfirm())
                {
                    break;
                }
                m_moveRow = index.row();
                m_pressedRow = index.row();
                m_tableView->clearSelection();
                emit deleteData(index);
                ((YjAddStructItemModel*)model)->removeSelItemRow(index.row());
                ((YjAddStructItemModel*)model)->onUpdataTable();
                break;
            }
            default:
                break;
            }
        }
    }else
    {
        switch (event->type())
        {
        // 鼠标滑过
        case QEvent::MouseMove:
        {
            m_nType = 0;
            if(index.row() ==  ((YjAddStructItemModel*)model)->getSelItem().size() -1 && model->data(index) != "")
            {
                    ((YjAddStructItemModel*)model)->addoneNewRow();
            }
            break;
        }
            // 鼠标按下
        case QEvent::MouseButtonPress:
        {
            m_nType = 1;
            break;
        }
            // 鼠标释放
        case QEvent::MouseButtonRelease:
        {
            bRepaint = true;
            m_moveRow = index.row();
            m_pressedRow = index.row();
            m_tableView->clearSelection();
            ((YjAddStructItemModel*)model)->onUpdataTable();
            break;
        }
        default:
            break;
        }
    }
    //    }
    ((YjAddStructItemModel*)model)->onUpdataTable();
    if (bRepaint)
    {
        return bRepaint;
    }
    else
    {
        return QStyledItemDelegate::editorEvent(event, model, option, index);
    }
}

void YjAddStructItemDelegate::setSelectRowClear()
{
    m_mousePoint = QPoint(-1, -1);
    m_pressedRow = -1;
    m_moveRow = -1;
}














原文地址:https://www.cnblogs.com/chouchouTT/p/11944916.html

随机文章