QCombox实现下拉树结构,QTreeView Model 数据层设置及实现代码

Qcombox拉下一树形结构显示,这里提供具体的实现逻辑,分别由 窗体、数据层管理结构设计、QAbstractItemModel,每一个节点的数据信息,实现代码,实现关键是setModel 和setView的使用

第一、每一个节点的数据结构形式

#pragma once
#include
#include
#include
#include
#include

class TreeViewDataInfo;

typedef QList TreeViewDataInfoList;
class TreeViewDataInfo
{
public:
    TreeViewDataInfo(TreeViewDataInfo* pParent = NULL);
    ~TreeViewDataInfo();
    int childCount() const;
    //存在子信息
    int childExists(const TreeViewDataInfo* pMap) const;
    TreeViewDataInfo* parentInfo() const;
public:
    QString m_TreeItmeName;
    TreeViewDataInfoList m_ChildList;  //子节点的集合
    TreeViewDataInfo* m_pParentInfo;  // 父节点信息
    int m_childCount;
};

第二、数据层管理管理设计

class TreeViewItem : public QObject
{
public:
    explicit TreeViewItem(TreeViewDataInfo* pMapInfo, TreeViewItem* pParent = NULL);
    ~TreeViewItem();
public:
    //获取父项
    TreeViewItem* parent() const;
    // 获取子项
    TreeViewItem* child(int nIndex) const;
    // 增加子项
    void appendChild(TreeViewItem* pItem);
    // 获取子项个数
    int childCount() const;
    //获取子项索引
    int childIndex(const TreeViewItem* pItem) const;
    // 删除子项
    bool removeChild(int nIndex);
    // 获取列数据
    QVariant data(int nCol, int role = Qt::DisplayRole);
    // 设置列数据
    bool setData(int nCol, QVariant val, int role = Qt::EditRole);
    //行号
    int row() const;
    //树Itme项的标志
    Qt::ItemFlags flags(int nCol) const;

    //获取数据
    TreeViewDataInfo* DataInfo() const;
    //交换子项位置
    bool swapChild(int nFirst, int nSecond);

private:
    TreeViewDataInfo* m_pDataInfo;                
    TreeViewItem* m_pParent;            // 父项
    QList m_childItems;  // 子项集合
                                            
};

第三、model数据的实现

class ComboxTreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    explicit ComboxTreeModel(QObject* parent = NULL);
    ~ComboxTreeModel();
    void updataModelTree(TreeViewDataInfoList MapList);  //更新treeView中的节点信息
    TreeViewItem *getRootItem() { return m_pRootItem; };
public:
    TreeViewItem* itemFromIndex(QModelIndex index) const;  //从index 里获取具体的item信息
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const { return 1; };
    virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
    virtual QModelIndex parent(const QModelIndex &child) const;
    virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    TreeViewItem* appendItem(TreeViewDataInfo* pDataInfo, TreeViewItem* pParentItem);
    QVariant bulidData(TreeViewItem* pItem, int nCol, int role = Qt::DisplayRole) const;
private:
    TreeViewDataInfoList m_mapInfos;
    TreeViewItem* m_pRootItem;
    TreeViewDataInfo* m_pForRoot;
};

第四、窗体类

class TestDialog : public QDialog
{
    Q_OBJECT
public:
    TestDialog(QWidget* parent);
    ~TestDialog();
    void refreshModelData(TreeViewDataInfoList DataList);
    void setDefaultSelectItem(QString strItemName =QString(""));//根据显示的item去设置默认的选项
private:
    void initUI();
private:
    ComboxTreeModel  *m_pTreeModel;
    QTreeView *m_pTreeView;
    QComboBox *m_pCombox;
};

第五、对应接口的的实现

#include "QcomboxTree.h"
#include
#include
#include
#include

(1)每一个节点的数据结构形式实现

TreeViewDataInfo::TreeViewDataInfo(TreeViewDataInfo* pParent /*= NULL*/):m_pParentInfo(pParent)
{
     m_TreeItmeName ="";
     m_ChildList.clear();
     m_childCount =-1;
}

TreeViewDataInfo::~TreeViewDataInfo()
{
    if (!m_ChildList.isEmpty())
    {
        TreeViewDataInfoList::iterator b = m_ChildList.begin();
        TreeViewDataInfoList::iterator e = m_ChildList.end();
        for (; b != e; b++)
        {
            TreeViewDataInfo* pInfo = *b;
            if (NULL != pInfo)
            {
                delete pInfo;
                pInfo = NULL;
            }
        }
        m_ChildList.clear();
    }
}

int TreeViewDataInfo::childCount() const
{
    return m_ChildList.size();
}

int TreeViewDataInfo::childExists(const TreeViewDataInfo* pDataInf) const
{
    TreeViewDataInfoList::const_iterator pos = std::find(m_ChildList.begin(), m_ChildList.end(), pDataInf);
    if (pos != m_ChildList.end())
    {
        return pos - m_ChildList.begin();
    }

    return -1;
}

TreeViewDataInfo* TreeViewDataInfo::parentInfo() const
{
    return m_pParentInfo;
}

(2)数据层管理层实现

TreeViewItem::TreeViewItem(TreeViewDataInfo* pMapInfo, TreeViewItem* pParent /*= NULL*/)
    :m_pDataInfo(pMapInfo),m_pParent(pParent)
{

}

TreeViewItem::~TreeViewItem()
{

}

TreeViewItem* TreeViewItem::parent() const
{
    return m_pParent;
}

TreeViewItem* TreeViewItem::child(int nIndex) const
{
    if (nIndex < 0 && nIndex >= childCount())
    {
        return NULL;
    }

    return m_childItems.at(nIndex);
}

void TreeViewItem::appendChild(TreeViewItem* pItem)
{
    assert(NULL != pItem && pItem->m_pParent == this);
    if (NULL != pItem)
    {
        m_childItems.append(pItem);
    }
}

int TreeViewItem::childCount() const
{
    return m_childItems.size();
}

int TreeViewItem::childIndex(const TreeViewItem* pItem) const
{
    assert(NULL != pItem);
    if (NULL == pItem || m_childItems.isEmpty())
    {
        return -1;
    }

    QList::const_iterator pos = std::find(m_childItems.begin(), m_childItems.end(), pItem);
    if (pos != m_childItems.end())
    {
        return pos - m_childItems.begin();
    }
    else
    {
        return -1;
    }
}

bool TreeViewItem::removeChild(int nIndex)
{
    if (nIndex < 0 || nIndex >= childCount())
    {
        return false;
    }

    TreeViewItem* pItem = child(nIndex);
    if (NULL == pItem)
    {
        return false;
    }

    if (NULL != pItem->m_pDataInfo)
    {
        if (NULL != m_pDataInfo)
        {
            int nPos = m_pDataInfo->childExists(pItem->m_pDataInfo);
            if (nPos >= 0 && nPos < m_pDataInfo->childCount())
            {
                m_pDataInfo->m_ChildList.removeAt(nPos);
            }
        }

        delete pItem->m_pDataInfo;
        pItem->m_pDataInfo = NULL;
    }

    delete pItem;
    pItem = NULL;

    m_childItems.removeAt(nIndex);
    return true;
}

QVariant TreeViewItem::data(int nCol, int role /*= Qt::DisplayRole*/)
{
    assert(NULL != m_pDataInfo);
    assert(0 <= nCol && nCol <= 3);

    if (NULL == m_pDataInfo)
    {
        return QVariant();
    }
    else if (Qt::TextAlignmentRole == role)
    {
        return int(Qt::AlignLeft);
    }
    else if (Qt::DisplayRole == role)
    {
        switch (nCol)
        {
        case 0: // get show name
            return m_pDataInfo->m_TreeItmeName;
            break;
        }
    }
    return QVariant();
}

bool TreeViewItem::setData(int nCol, QVariant val, int role /*= Qt::EditRole*/)
{

}

int TreeViewItem::row() const
{
    if (NULL == m_pParent)
    {
        return 0;
    }
    else
    {
        return m_pParent->m_childItems.indexOf(const_cast(this));
    }
}

Qt::ItemFlags TreeViewItem::flags(int nCol) const
{
    if (NULL == m_pDataInfo)
    {
        return Qt::NoItemFlags;
    }
    if (1 == nCol)
    {
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
    }
    else
    {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
    }
}

TreeViewDataInfo* TreeViewItem::DataInfo() const
{
    return m_pDataInfo;
}

bool TreeViewItem::swapChild(int nFirst, int nSecond)
{
    assert(0 <= nFirst && nFirst < childCount());
    assert(0 <= nSecond && nSecond < childCount());

    if (nFirst < 0 || nFirst >= childCount()
        || nSecond < 0 || nSecond >= childCount())
    {
        return false;
    }

    m_childItems.swap(nFirst, nSecond);

    if (NULL != m_pDataInfo
        && m_pDataInfo->m_ChildList.size() > nFirst
        && m_pDataInfo->m_ChildList.size() > nSecond)
    {
        m_pDataInfo->m_ChildList.swap(nFirst, nSecond);
    }

    return true;
}

(3)数据的model实现

ComboxTreeModel::ComboxTreeModel(QObject* parent /*= NULL*/)
{
    m_pForRoot = new TreeViewDataInfo();
    m_pRootItem = new TreeViewItem(m_pForRoot);
}

ComboxTreeModel::~ComboxTreeModel()
{

}

void ComboxTreeModel::updataModelTree(TreeViewDataInfoList MapList)
{
    beginResetModel();
    m_mapInfos = MapList;
    if (m_mapInfos.isEmpty())
    {
        return;
    }
    if (NULL != m_pForRoot)
    {
        delete m_pForRoot;
        m_pForRoot = NULL;
    }
    if (NULL != m_pRootItem)
    {
        delete m_pRootItem;
        m_pRootItem = NULL;
    }
    bool bHasFind = false;
    m_pForRoot = new TreeViewDataInfo();
    m_pRootItem = new TreeViewItem(m_pForRoot);
    assert(NULL != m_pRootItem);
    //更新树里的数据,重新创建树的Itme项
    for (int i = 0; i < m_mapInfos.size(); i++)
    {
        TreeViewDataInfo* pMap = m_mapInfos.at(i);
        assert(NULL != pMap);
        if (NULL != pMap->m_pParentInfo)
        {
            continue;
        }
        appendItem(pMap, m_pRootItem);
    }
    endResetModel();
}

TreeViewItem* ComboxTreeModel::itemFromIndex(QModelIndex index) const
{
    if (!index.isValid())
    {
        return m_pRootItem;
    }
    else
    {
        return static_cast(index.internalPointer());
    }
}

int ComboxTreeModel::rowCount(const QModelIndex &parent /*= QModelIndex()*/) const
{
    if (parent.column() > 0)
    {
        return 0;
    }

    TreeViewItem* pParent = itemFromIndex(parent);

    assert(NULL != pParent);
    if (NULL != pParent)
    {
        return pParent->childCount();
    }
    else
    {
        return 0;
    }
}

QModelIndex ComboxTreeModel::index(int row, int column, const QModelIndex &parent /*= QModelIndex()*/) const
{
    assert(NULL != m_pRootItem);
    if (!hasIndex(row, column, parent))
    {
        return QModelIndex();
    }

    TreeViewItem* pParentItem = itemFromIndex(parent);

    assert(NULL != pParentItem);
    if (NULL != pParentItem)
    {
        TreeViewItem* pItem = pParentItem->child(row);
        QModelIndex oCreateIndex = createIndex(row, 0, pItem);
        return oCreateIndex;
    }

    return QModelIndex();
}

QModelIndex ComboxTreeModel::parent(const QModelIndex &child) const
{
    TreeViewItem* pItem = itemFromIndex(child);
    assert(NULL != pItem);

    TreeViewItem* pParentItem = pItem->parent();
    if (NULL == pParentItem)
    {
        return QModelIndex();
    }
    else if (pParentItem == m_pRootItem)
    {
        return QModelIndex();
    }
    else
    {
        QModelIndex oCreateIndex = createIndex(pParentItem->row(), 0, pParentItem);
        return oCreateIndex;
    }
}

QVariant ComboxTreeModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const
{
    if (!index.isValid())
    {
        return QVariant();
    }

    TreeViewItem* pItem = static_cast(index.internalPointer());
    assert(NULL != pItem);

    if (NULL != pItem)
    {
        return bulidData(pItem, index.column(), role);
    }
    else
    {
        return QVariant();
    }
}

Qt::ItemFlags ComboxTreeModel::flags(const QModelIndex &index) const
{
    TreeViewItem* pItem = itemFromIndex(index);
    assert(NULL != pItem);

    if (NULL != pItem)
    {
        return pItem->flags(index.column());
    }
    else
    {
        return Qt::NoItemFlags;
    }
}

TreeViewItem* ComboxTreeModel::appendItem(TreeViewDataInfo* pDataInfo, TreeViewItem* pParentItem)
{
    assert(NULL != pDataInfo && NULL != pParentItem);
    if (NULL == pDataInfo || NULL == pParentItem)
    {
        return NULL;
    }
    //增加Item项
    TreeViewItem* pItem = new TreeViewItem(pDataInfo, pParentItem);
    assert(NULL != pItem);
    if (NULL == pItem)
    {
        return NULL;
    }
    pParentItem->appendChild(pItem);

    if (!pDataInfo->m_ChildList.isEmpty())
    {
        for (int i = 0; i < pDataInfo->m_ChildList.size(); i++)
        {
            TreeViewDataInfo* pChildMap = pDataInfo->m_ChildList.at(i);
            assert(NULL != pChildMap);

            if (NULL != pChildMap)
            {
                //树里不断增加子树
                appendItem(pChildMap, pItem);
            }
        }
    }
    return pItem;
}

QVariant ComboxTreeModel::bulidData(TreeViewItem* pItem, int nCol, int role /*= Qt::DisplayRole*/) const
{
    assert(NULL != pItem->DataInfo());
    QString strShowName("");
    if (NULL == pItem->DataInfo())
    {
        return QVariant();
    }
    else if (Qt::TextAlignmentRole == role)
    {
        return QVariant(Qt::AlignVCenter | Qt::AlignLeft);
    }
    else if (Qt::DisplayRole == role)
    {
        switch (nCol)
        {
        case 0:
            strShowName = pItem->DataInfo()->m_TreeItmeName;
            return strShowName;
        default:
            break;
        }
    }
    return QVariant();
}

(4)窗体类的实现

TestDialog::TestDialog(QWidget* parent):QDialog(parent) //半自动化释放内存
{
    initUI();
}

TestDialog::~TestDialog()
{

}

void TestDialog::refreshModelData(TreeViewDataInfoList DataList)
{
    if (m_pTreeModel == nullptr || m_pTreeView == nullptr)
        return;
    m_pTreeModel->updataModelTree(DataList);  //根据新的数据更新combox下的下拉树
    m_pTreeView->expandAll(); //全部展开
    setDefaultSelectItem();  //设置默认选中项
}

void TestDialog::setDefaultSelectItem(QString strItemName)
{
    QAbstractItemModel *model = m_pCombox->view()->model();
    QModelIndexList Items = model->match(model->index(0, 0), Qt::DisplayRole, QVariant::fromValue(strItemName), 2, Qt::MatchRecursive);
    //这种做法解决combox中存在TreeView视图时刷洗设置二级以下节点为当前默认显示项Index无法显示正确信息的问题。
    //如果需要将 子树下的第二 第三级等的子树设置为默认选项,需要这样子设置
    if (Items.size() > 0)
    {
         for (QModelIndex m_oRightIndex : Items)
        {
            m_pCombox->setRootModelIndex(m_oRightIndex.parent());
            m_pCombox->setModelColumn(m_oRightIndex.column());
            m_pCombox->setCurrentIndex(m_oRightIndex.row());
            m_pCombox->setRootModelIndex(QModelIndex());
            m_pCombox->view()->setCurrentIndex(m_oRightIndex);
         }
    }
}

void TestDialog::initUI()
{
    QHBoxLayout* pLayOut = new QHBoxLayout;
    pLayOut->setContentsMargins(16, 10, 16, 10); //设置布局左右顶底距离的位置
    m_pCombox = new QComboBox(this);
    m_pTreeModel = new ComboxTreeModel();
    m_pTreeView = new QTreeView(m_pCombox);
    m_pTreeView->setHeaderHidden(true);
    m_pTreeView->header()->hide();   //不显示头的行列
    m_pTreeView->header()->hideSection(0);
    pLayOut->addWidget(m_pCombox);
    m_pCombox->setModel(m_pTreeModel); //将model设入树combox中
    m_pCombox->setView(m_pTreeView); //将treeView设入combox中
    this->setLayout(pLayOut);  //将布局加入到窗体中
}

 

 

你可能感兴趣的:(Qt,界面,C++,前端,c++,qt,树结构)