注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

沙漠里de烟雨

原创分享,禁止转载

 
 
 

日志

 
 

Qt 之 QTreeView与QAbstractItemModel下的自定义树,支持拖放  

2017-05-29 14:31:24|  分类: QT5.x与QML |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
如下图所示
Qt 之 QTreeView与QAbstractItemModel下的自定义树,支持拖放 - 漠雨 - 沙漠里de烟雨__风尘无名
 
 代码如下:
widget.cpp =>

#include "Widget.h"
#include <QLayout>
#include <QTreeWidget>
#include <QTreeView>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMimeData>
#include "TreeModel.h"
#include "TreeView.h"

Widget::Widget(QWidget *parent) : QWidget(parent)
{
    resize(800,600);
 //   this->setAttribute(Qt::WA_TranslucentBackground, true);

    setAcceptDrops(true);

    //tree widget;
    TreeView * treeView = new TreeView(this);
    TreeModel* model = new TreeModel;
    treeView->setModel(model);


    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(treeView);
    this->setLayout(layout);

}

Widget::~Widget()
{

}


TreeNodeData.h =>

#ifndef TREENODEDATA_H
#define TREENODEDATA_H
#include <QObject>
enum enumType
{
    CONTAINER = 0,
    OBJECT
};
typedef struct tagTreeNodeData
{
public:
    tagTreeNodeData()
    {
        this->type = CONTAINER;
        this->enable = true;
        this->check = 0;
        this->name = "undefined";
        this->object = 0;
    }
    tagTreeNodeData& operator=(const tagTreeNodeData& other)
    {
        if(this == &other)
            return *this;
        this->type = other.type;
        this->enable = other.enable;
        this->check = other.check;
        this->name = other.name;
        this->object = other.object; //浅拷贝;
    }
    tagTreeNodeData(const QString& name,int type = CONTAINER,
                    bool enable=true,int check=0,QObject* object = 0)
    {
        this->name = name;
        this->enable = enable;
        this->check = check;
        this->type = type;
        this->object = object;
    }
    int         type;
    bool        enable; // 是否可进行勾选操作;
    int         check; // 0表示未勾选;1表示未全勾选;2表示已勾选;
    QString     name;
    QObject*    object;
}TreeNodeData;

#endif // TREENODEDATA_H

treeNode.h =>

#ifndef TREENODE_H
#define TREENODE_H
#include <QObject>
#include <QString>
#include <QList>
#include "TreeNodeData.h"
class TreeNode
{
public:
    TreeNode(TreeNode* parent = 0);
    TreeNode(TreeNode* otherNode, TreeNode* parent = 0);
    TreeNode(const TreeNodeData& data, TreeNode* parent = 0);
     ~TreeNode();

    bool appendChild(TreeNode* child);
    void setParent(TreeNode* parent);
    bool insertChildren(int pos,int count,const QVector<TreeNode*>& nodeVect);
    bool removeChildren(int pos,int count,QVector<TreeNode*>& nodeVect);
    int childCount() const;
    int index(TreeNode* child) const;
    TreeNode* child(uint index);
    TreeNode* parent();
    TreeNodeData  data;
private:
    TreeNode*           m_parent;
    QList<TreeNode*>    m_children;
};
#endif // TREENODE_H

TreeNode.cpp =>

#include "TreeNode.h"
#include <QDebug>
TreeNode::TreeNode(TreeNode *parent)
{
    this->m_parent = parent;
}

TreeNode::TreeNode(TreeNode *otherNode, TreeNode *parent)
{
    this->data = otherNode->data;
    this->m_parent = parent;
    if(otherNode->childCount()>0)
    {
        for(int i=0;i<otherNode->childCount();i++)
        {
           TreeNode* node = new TreeNode(otherNode->child(i),this);
           m_children.append(node);
        }
    }
}

TreeNode::TreeNode(const TreeNodeData& data, TreeNode *parent)
{
    this->data = data;
    this->m_parent = parent;
}

TreeNode::~TreeNode()
{
    m_parent = 0;
 //   qDeleteAll(m_children);
    foreach(TreeNode* node,m_children)
        delete node;
    m_children.clear();
}

bool TreeNode::appendChild(TreeNode *child)
{
    if(child && child!=this && child!=m_parent)
    {
        child->setParent(this);
        m_children.append(child);
        return true;
    }
    return false;
}

void TreeNode::setParent(TreeNode *parent)
{
    if(parent!=m_parent && parent!=this)
        m_parent = parent;
}

bool TreeNode::insertChildren(int pos, int count, const QVector<TreeNode*>& nodeVect)
{
    if(pos<0 || count<=0 || pos>childCount() || nodeVect.count()!=count)
        return false;

    for(int i=0;i<count;i++)
    {
        TreeNode* node = nodeVect[i];
        node->setParent(this);
        m_children.insert(pos++,node);
    }
    return true;
}

bool TreeNode::removeChildren(int pos, int count, QVector<TreeNode *> &nodeVect)
{
    if(pos<0 || pos+count>childCount())
        return false;
    for(int i=0;i<count;i++)
        nodeVect.append(m_children.takeAt(pos));
    return true;
}

int TreeNode::childCount() const
{
    return m_children.count();
}

int TreeNode::index(TreeNode *child) const
{
    if(!child) return -1;
    if(m_children.contains(child))
        return m_children.indexOf(child);
    return -1;
}

TreeNode *TreeNode::child(uint index)
{
    if(index < m_children.count())
        return m_children.at(index);
    return 0;
}

TreeNode *TreeNode::parent()
{
    return m_parent;
}

TreeModel.h =>

#ifndef TREEMODEL_H
#define TREEMODEL_H

#include <QList>
#include <QModelIndex>
#include <QAbstractItemModel>
#include "TreeNode.h"

class TreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    TreeModel(QObject* parent = 0);
    ~TreeModel();

    //must be rewrited!
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const ;
    QModelIndex parent(const QModelIndex &child) const;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;

    QHash<int, QByteArray> roleNames() const;
    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);

    QMimeData *mimeData(const QModelIndexList &indexes) const;
    QStringList mimeTypes() const;
    bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const;
    bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);

    Qt::DropActions supportedDragActions() const;
    Qt::DropActions supportedDropActions() const;

    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
//  //  bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
    bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());

public:
    TreeNode* root();
    TreeNode* node(const QModelIndex& index);
    QModelIndex index(TreeNode* node);

    void deleteTmpVect(); //从vector中移除后并释放所有节点数据;
    void clearTmpVect(); //只是从vector中移除所有节点但并不释放内存;
    bool appendNodeToTmpVect(TreeNode* node);
private:
    void        printTree(TreeNode* parentNode,int spaceLen);
    void        appendChildren(TreeNode *parent);
    TreeNode*   createNode(const QString& name,int type);
    TreeNode*   nodeFromIndex(const QModelIndex & index) const;
    QModelIndex indexFromNode(TreeNode* node) const;
    TreeNode*   setParentsCheckState(TreeNode* node);
    TreeNode*   setChildrenCheckState(TreeNode* parent);
    void updateParentsCheckStateAfterInserted(TreeNode* parentNode,int newChkState);
    void updateParentsCheckStateAfterRemoved(TreeNode* parentNode);
private:
    TreeNode *  m_root;
    QVector<TreeNode*>  m_tmpVect;
};

#endif // TREEMODEL_H

TreeModel.cpp =>

#include "TreeModel.h"

#include <QDebug>
#include <QtCore>
#include <QIcon>
#include <QBrush>
#include <QColor>
#include <QByteArray>
#include <QDataStream>
#include <QTextCodec>
#include <iostream>

TreeModel::TreeModel(QObject *parent) : QAbstractItemModel(parent)
{
    m_root = new TreeNode(TreeNodeData("root"));

    TreeNode* item1 = new TreeNode(TreeNodeData("item1 name"));
    appendChildren(item1);

    TreeNode* item2 = new TreeNode(TreeNodeData("item2 name"));
    appendChildren(item2);

    m_root->appendChild(item1);
    m_root->appendChild(item2);


//    printTree(m_root,0);
}

TreeModel::~TreeModel()
{
    delete m_root;
}

void TreeModel::appendChildren(TreeNode *parent)
{
    if(!parent)
        return ;
    parent->appendChild(createNode("name1",CONTAINER));
    parent->appendChild(createNode("name2",CONTAINER));
    parent->appendChild(createNode("name3",CONTAINER));
    parent->appendChild(createNode("name4",CONTAINER));
    parent->appendChild(createNode("name5",CONTAINER));
    parent->appendChild(createNode("name6",CONTAINER));
}
TreeNode *TreeModel::createNode(const QString &name,int type)
{
    TreeNode* node = new TreeNode(TreeNodeData(name,type));
    node->appendChild(new TreeNode(TreeNodeData("name 1",OBJECT)));
    node->appendChild(new TreeNode(TreeNodeData("name 2",OBJECT)));
    node->appendChild(new TreeNode(TreeNodeData("name 3",OBJECT)));
    node->appendChild(new TreeNode(TreeNodeData("name 4",OBJECT)));
    node->appendChild(new TreeNode(TreeNodeData("name 5",OBJECT)));
    node->appendChild(new TreeNode(TreeNodeData("name 6",OBJECT)));
    return node;
}

int TreeModel::rowCount(const QModelIndex &parent) const
{
    if(parent.isValid())
    {
        TreeNode* parentNode = nodeFromIndex(parent);
        if(!parentNode)
            return 0;
        return parentNode->childCount();
    }
    return m_root->childCount();
}

int TreeModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}

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

    TreeNode* node = nodeFromIndex(index);
    if(!node)
        return QVariant();

    if(role == Qt::DecorationRole)
    {
        switch(node->data.type)
        {
        case CONTAINER:
            return QIcon("pic/folder.ico");
        case OBJECT:
            return QIcon("pic/Qt.ico");
        }
    }
    if(role == Qt::DisplayRole)
    {
        return node->data.name;
    }
    if(role == Qt::EditRole)
    {
        return node->data.name;
    }
    if(role == Qt::CheckStateRole)
    {
        return node->data.check;
    }
//    if(role == Qt::FontRole) //字体;
//    {
//        QFont font;
//        font.setItalic(true);
//        return  font;
//    }
    if(role == Qt::BackgroundRole) //背景颜色;
    {
        return QBrush(Qt::white);
    }
    if(role == Qt::ForegroundRole) //字体颜色;
    {
        return QBrush("black");
    }
    if(role == Qt::ToolTipRole) //提示;
    {
        return "tooltip";
    }

    return QVariant();
}

QModelIndex TreeModel::parent(const QModelIndex &child) const
{
    if(!child.isValid())
        return QModelIndex();

    TreeNode* childNode = nodeFromIndex(child);
    if(!childNode)
        return QModelIndex();

    TreeNode* parentNode = childNode->parent();
    if(!parentNode)
        return QModelIndex();

    if(parentNode == m_root)
        return QModelIndex();

    TreeNode* grandParentNode = parentNode->parent();
    if(!grandParentNode)
        return QModelIndex();

    int row = grandParentNode->index(parentNode);

    return createIndex(row,0,parentNode); //绑定TreeNode*与QModelIndex,使之QModelIndex.interalPointer()==TreeNode*;
}

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{    
    if(!m_root
        || (column<0 || column>=columnCount(parent))
        || (row<0 || row>=rowCount(parent))
      ) return QModelIndex();

    if(!hasIndex(row,column,parent))
        return QModelIndex();

    TreeNode* parentNode = m_root;
    if(parent.isValid()) //父节点可见(非根节点);
        parentNode = nodeFromIndex(parent);

    TreeNode* childNode = parentNode->child(row);
    if(!childNode) //查勘是否存在这样的节点;
        return QModelIndex();

    return createIndex(row,column,childNode); //数据树中存在这样的节点,则在model中创建与此对应的QModelIndex节点;
}

QHash<int, QByteArray> TreeModel::roleNames() const
{
    QHash<int, QByteArray> roleNamesHash(QAbstractItemModel::roleNames());
    roleNamesHash[Qt::DisplayRole] = "name";
    return roleNamesHash;
}

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return Qt::NoItemFlags ;
    return QAbstractItemModel::flags(index) |
            Qt::ItemIsSelectable |
            Qt::ItemIsEditable |
            Qt::ItemIsUserCheckable |
            Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;

//    Qt::ItemFlags defaultFlags = QStringListModel::flags(index);
//    if (index.isValid())
//        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
//    else
//        return Qt::ItemIsDropEnabled | defaultFlags;
}

bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    TreeNode* node = nodeFromIndex(index);

    if(role == Qt::EditRole)
    {
        node->data.name = value.toString();
        emit dataChanged(index,index);
    }

    if(role == Qt::CheckStateRole)
    {
        node->data.check = value.toInt();
        emit dataChanged(index,index);

        TreeNode* firstNode = setParentsCheckState(node); //向上溯,处理父节点直至根;
        QModelIndex leftTopIndex = indexFromNode(firstNode);
        QModelIndex rightBottomIndex = index;
        if(node->childCount()>0) //若有子节点,往下溯;
        {
            TreeNode* lastNode = setChildrenCheckState(node);
            rightBottomIndex = indexFromNode(lastNode);
        }

        emit dataChanged(leftTopIndex,rightBottomIndex);
    }

    return true;
}

QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{ //drag = flags()中Qt::ItemIsDragEnabled + mimeData();   //封装Drag数据;

    if(indexes.count()<=0)
        return 0;

    QMimeData* mimeData = new QMimeData;
    QByteArray itemData;
    QDataStream dataStream(&itemData,QIODevice::WriteOnly);

    foreach(QModelIndex index,indexes)
    { //用于同时选择多个节点;
        if(index.isValid())
        {
             TreeNode* node = nodeFromIndex(index);
             if(!node) continue;
             qint32 node_ptr = reinterpret_cast<qint32>(node);
             QString ptr_str = QString::number(node_ptr);
             dataStream << ptr_str;
        }
    }

    mimeData->setData("application/between.in.tree",itemData);

    return mimeData;
}

QStringList TreeModel::mimeTypes() const
{//drop = flags()中Qt::ItemIsDropEnabled + dropMimeData();
 //返回当前数据模型允许接收的数据类型列表,它会在Drag操作过程中被调用;如果没有相关类型,则不允许Drop操作;
  //  QStringList strList("text/uri-list");// text/uri-list // application/x-qabstractitemmodeldatalist
    QStringList strList;
    strList << "application/between.in.tree"; //当前TreeView之间移动;
    strList << "text/uri-list"; //来自外来的数据;
    return strList;
}

bool TreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
    if(action == Qt::IgnoreAction)
        return false;

    if(column >= columnCount(parent))
        return false;

    if(!data || (!data->hasFormat("application/between.in.tree") && !data->hasFormat("text/uri-list")) )
        return false;

    if(data->hasFormat("application/between.in.tree"))
    {
        TreeNode* parentNode = nodeFromIndex(parent);
        if(!parentNode)
            return false;

        if(row<0 && parentNode->data.type!=CONTAINER) //被挂靠时(row<0)必须是CONTAINER;被插入(row>=0)时则没有此限制;
            return false;
    }
    else
    {

        QByteArray buf = data->data("text/uri-list");
        QString p = QString::fromUtf8(buf);
        qDebug() << "can drop data:" << buf << endl;

        return data->hasUrls();
    }

    return true;
}
bool TreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{ //解析数据并将其添加到当前模型的合适位置;

 //   if(!canDropMimeData(data,action,row,column,parent))
 //       return false;

    //TreeView中节点之间的拖动;
    QByteArray encodeData = data->data("application/between.in.tree");
    if(!encodeData.isEmpty())
    {
        QDataStream dataStream(&encodeData,QIODevice::ReadOnly);

        QList<TreeNode*> nodeList;
        while(!dataStream.atEnd())
        {  //用于处理多个节点;注意:当同时选择了子节点的同时也选择了父节点时,应该取消子节点的选择,因为父节点已被选择。
            QString ptr_str;
            dataStream >> ptr_str;
            qint32 node_ptr = ptr_str.toInt();
            TreeNode* node = reinterpret_cast<TreeNode*>(node_ptr);
            if(!node) continue;
            nodeList.append(node);
        }
        if(nodeList.count()<=0)
            return false;

        //因为是内部间移动,则先移除;
        bool removeOK = false;
        foreach(TreeNode* node, nodeList)
        {
            TreeNode* parentNode = node->parent();
            removeOK |= removeRows(parentNode->index(node),1,indexFromNode(parentNode));
        }

        if(removeOK && m_tmpVect.count()>0)
            insertRows(row,nodeList.count(),parent);

        return true;
    }

    //从外部拖进来的文件;
    //encodeData = data->data("text/uri-list");
    foreach(QUrl url,data->urls())
    {
        QString name = url.fileName();
        m_tmpVect.append(new TreeNode(TreeNodeData(name,CONTAINER)));
    }

    if(m_tmpVect.count()>0)
    {
        TreeNode* node = nodeFromIndex(parent);
        QModelIndex parentIndex = parent;
        if(row<0 && node->data.type!=CONTAINER) //如果挂靠时parent为非CONTAINER;
        {
            row = node->parent()->index(node)+1;
            parentIndex = this->parent(parent);
        }
        insertRows(row,m_tmpVect.count(),parentIndex);
    }

    return true;
}

Qt::DropActions TreeModel::supportedDragActions() const
{
    return Qt::CopyAction|Qt::MoveAction; //Qt::CopyAction要加上,否则提示不可拖放;
}

Qt::DropActions TreeModel::supportedDropActions() const
{
    return Qt::CopyAction|Qt::MoveAction;
}

bool TreeModel::removeRows(int row, int count, const QModelIndex &parent)
{
    TreeNode* parentNode = nodeFromIndex(parent);
    if(!parentNode)
        return false;

    bool ok = false;
    beginRemoveRows(parent,row,row+count-1);
    ok = parentNode->removeChildren(row,count,m_tmpVect);
    if(ok)
    { //此时结构已经变了,应向上回溯直至根,更新所有的父节点的选中状态;
        updateParentsCheckStateAfterRemoved(parentNode);
    }
    endRemoveRows();

    return ok;
}

bool TreeModel::insertRows(int row, int count, const QModelIndex &parent)
{
    TreeNode* parentNode = nodeFromIndex(parent);
    if(!parentNode)
        return false;

    if(row<0) //row<0把待插入的节点当做子节点挂靠在parentNode下(“框选某节点的标识”);//row>=0插入到以parentNode为父节点的子队列中,相当于调整排序(“一根横线的插入标识”);
    {
        row = parentNode->childCount();
    }

    bool ok = false;
    beginInsertRows(parent,row,row+count-1);
    ok = parentNode->insertChildren(row,count,m_tmpVect);
    if(ok)
    {//此时结构已经变了,应向上回溯直至根,更新所有的父节点的选中状态;
        int newChkState = Qt::Unchecked;
        int checkedSum = 0;
        foreach(TreeNode* node,m_tmpVect)
        {
            if(node->data.check == Qt::PartiallyChecked)
            {
                checkedSum = -1;
                break;
            }
            else if(node->data.check == Qt::Checked)
            {
                checkedSum++;
            }
        }

        if(checkedSum == m_tmpVect.count())
            newChkState = Qt::Checked;
        else if(checkedSum == 0)
            newChkState = Qt::Unchecked;
        else
            newChkState = Qt::PartiallyChecked;

        if(parentNode != m_root)
            updateParentsCheckStateAfterInserted(parentNode,newChkState);
    }

    endInsertRows();

    m_tmpVect.clear(); //被移动节点列表当功已成身退;其实也是多此一举,若无意外则为空;

    return ok;
}

TreeNode *TreeModel::root()
{
    return m_root;
}

TreeNode *TreeModel::node(const QModelIndex &index)
{
    return nodeFromIndex(index);
}

QModelIndex TreeModel::index(TreeNode *node)
{
    return indexFromNode(node);
}

void TreeModel::deleteTmpVect()
{
    foreach(TreeNode* node,m_tmpVect)
        delete node;
    m_tmpVect.clear();
}

void TreeModel::clearTmpVect()
{
    m_tmpVect.clear();
}

bool TreeModel::appendNodeToTmpVect(TreeNode *node)
{
    if(!node) return false;
    m_tmpVect.append(node);
    return true;
}

void TreeModel::printTree(TreeNode* parentNode,int spaceLen)
{//尾递归;
    if(!parentNode)
        return ;

    int nChildCount = parentNode->childCount();

    for(int i=0;i<spaceLen;i++)
    {
        if(spaceLen==1)
        {
            std::cout << "+";
            break;
        }
        if(i==0)
            std::cout << "|";
        else
            std::cout << "---";
    }

    if(nChildCount==0)
    {
        std::cout << parentNode->data.name.toStdString() << std::endl;
        return ;
    }

    std::cout << parentNode->data.name.toStdString() << std::endl;
    for(int i=0;i<nChildCount;i++)
    {
        printTree(parentNode->child(i),spaceLen+1);
    }
}

TreeNode *TreeModel::nodeFromIndex(const QModelIndex &index) const
{
    if(index.isValid())
        return static_cast<TreeNode*>(index.internalPointer());
    else
        return m_root;
}

QModelIndex TreeModel::indexFromNode(TreeNode *node) const
{
    if(!node || node==m_root)
        return QModelIndex();

    int row = node->parent()->index(node);
    return createIndex(row,0,node);
}

TreeNode* TreeModel::setParentsCheckState(TreeNode *node)
{
    if(!node) return 0;

    if(node == m_root)
        return node;

    TreeNode* parentNode = node->parent();
    if(parentNode == m_root)
    {
        return node;
    }
    else
    {
        int allUnchecked = 0, allChecked = 0;
        int nChildCount = parentNode->childCount();
        for(int i=0;i<nChildCount;i++)
        {
            int chk = parentNode->child(i)->data.check;
            allUnchecked += chk; //if chk均=0, => allUnchecked=0;
            allChecked += (chk-2); //if chk均=2, => allChecked=0;
        }

        if(allUnchecked==0) //全未选中;
            parentNode->data.check = 0;
        else if(allChecked==0) //全选中;
            parentNode->data.check = 2;
        else //一部分选中;
            parentNode->data.check = 1;
        return setParentsCheckState(parentNode);
    }
}

TreeNode* TreeModel::setChildrenCheckState(TreeNode *parent)
{
    if(!parent) return 0;
    TreeNode* lastNode = parent;
    int parentChkState = parent->data.check;
    int nChildCount = parent->childCount();
    if(nChildCount>0)
    {
        for(int i=0;i<nChildCount;i++)
        {
            TreeNode* childNode = parent->child(i);
            childNode->data.check = parentChkState;
            lastNode = setChildrenCheckState(childNode);
        }
    }

    return lastNode;
}

void TreeModel::updateParentsCheckStateAfterInserted(TreeNode *parentNode,int newChkState) //parentNode为被奉为父节点(被插入节点)的节点,newChkState为新添加的子节点的总的选择状态;
{
    if(!parentNode) return ;
    switch(parentNode->data.check) //未插入之前的状态;
    {
    case Qt::Unchecked: //新添加的子节点中只要存在选中的,则父节点为部分选中状态PartiallyChecked;
        parentNode->data.check = (newChkState==Qt::Unchecked) ? Qt::Unchecked : Qt::PartiallyChecked;
        break;
    case Qt::PartiallyChecked: //说明原先就为未全选状态(肯定为非叶节点),所以新添加的子节点的状态对其没有影响;
        break;
    case Qt::Checked: //新添加的状态如果不是全选状态,就会把父节点的全选状态变为未全选状态;
        parentNode->data.check = (newChkState==Qt::Checked) ? Qt::Checked : Qt::PartiallyChecked;
        break;
    }
    TreeNode* begNode = setParentsCheckState(parentNode);
    TreeNode* endNode = parentNode;
    emit dataChanged(index(begNode),index(endNode)); //更新状态;
}

void TreeModel::updateParentsCheckStateAfterRemoved(TreeNode *parentNode)
{
    if(!parentNode) return ;
    if(parentNode->childCount()<=0 && parentNode->data.check==Qt::PartiallyChecked) //子节点被移光,成叶节点了,故只能有两种状态;
    {
        parentNode->data.check = Qt::Unchecked;
    }
    else
    {
        int checkedSum = 0;
        for(int i=0;i<parentNode->childCount();i++)
        {
            if(parentNode->child(i)->data.check == Qt::PartiallyChecked)
            {
                checkedSum = -1;
                break;
            }
            if(parentNode->child(i)->data.check == Qt::Checked)
                checkedSum++;
        }
        if(checkedSum == 0)
            parentNode->data.check = Qt::Unchecked;
        else if(checkedSum == parentNode->childCount())
            parentNode->data.check = Qt::Checked;
        else
            parentNode->data.check = Qt::PartiallyChecked;
    }
    TreeNode* begNode = setParentsCheckState(parentNode);
    TreeNode* endNode = parentNode;
    emit dataChanged(index(begNode),index(endNode)); //更新状态;
}


TreeView.h =>


#ifndef TREEVIEW_H
#define TREEVIEW_H

#include <QTreeView>

class TreeNode;
class TreeView : public QTreeView
{
    Q_OBJECT
public:
    TreeView(QWidget* parent=0);
protected:
    void mousePressEvent(QMouseEvent * event);
    void mouseReleaseEvent(QMouseEvent *event);

private slots:
    void slotCustomContextMenu(const QPoint& pt);
    void slotCreateGroup();
    void slotNewItem();
    void slotDeleteItem();
    void slotRenameItem();
private:
    QModelIndex getIndex(const QModelIndex& parentIndex,int& row) const;
    void performDrag();
    void createNode(const QString& name, int type);
    bool isParentNodeSelected(const QModelIndexList& selectedList,const QModelIndex& index);
    void cancelChildrenNodeSelected(const QModelIndexList& selectedList,const QModelIndex& index);
    void resetSelectStateWhileShiftHolding(const QModelIndexList& selectedList);
    void getTopestParentSelected(const QModelIndexList& selectedList,const QModelIndex& index,QModelIndex& topestIndex);
private:
    QMenu*      m_popmenu;
    TreeNode*   m_selNode;
    QPoint      m_startPos;
    QString     m_dragString;

};

#endif // TREEVIEW_H

TreeView.cpp =>

#include "TreeView.h"
#include "TreeModel.h"
#include "TreeNode.h"
#include <QMenu>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QApplication>
#include <QMimeData>
#include <QDrag>
#include <QDebug>

TreeView::TreeView(QWidget *parent) : QTreeView(parent)
{
    this->setContextMenuPolicy(Qt::CustomContextMenu);
    this->setEditTriggers(QAbstractItemView::SelectedClicked); //must; otherwise, double clicked to editing start.(about this->edit(QModelIndex,...))
    this->setDragDropMode(QTreeView::DragDrop); // == this->setDragEnabled(true); 并且  this->setAcceptDrops(true);
    this->setDropIndicatorShown(true);
    this->setDefaultDropAction(Qt::MoveAction);
    this->setHeaderHidden(true);
    this->setSelectionMode(QAbstractItemView::ExtendedSelection); //MultiSelection可多选;SingleSelection
  //  this->setDragDropMode(QAbstractItemView::InternalMove); //treeView内部之间的移动;
    this->setFocusPolicy(Qt::StrongFocus);
    this->setStyleSheet("QTreeView::focus{border: 2px solid blue;}");
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);

    m_selNode = 0;
    m_popmenu = new QMenu(this);
    m_popmenu->addAction(QString("Create Group"),this,SLOT(slotCreateGroup()));
    m_popmenu->addAction(QString("New Item"),this,SLOT(slotNewItem()));
    m_popmenu->addAction(QString("Delete Item"),this,SLOT(slotDeleteItem()));
    m_popmenu->addAction(QString("Rename Item"),this,SLOT(slotRenameItem()));

    connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(slotCustomContextMenu(const QPoint&)));
    //   connect(this,SIGNAL(clicked(QModelIndex)),this,SLOT(slotClicked(QModelIndex)));
}

void TreeView::mousePressEvent(QMouseEvent *event)
{
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());
    QModelIndex index = indexAt(event->pos());

    if(event->button()&Qt::RightButton)
    {
        m_selNode = treeModel->node(index);
    }

    if(event->button()&Qt::LeftButton && index.isValid())
    {
        if(QApplication::keyboardModifiers() == Qt::ControlModifier) //按住Ctrl键;
        {
            QModelIndexList indexList = this->selectedIndexes(); //在单击之前所有被选择出来的;
            if(!indexList.contains(index)) //当前被选择的项;待包含进SelectedIndexes中;
            {//检查当前被选择的项是否其上层已被选择,下层是否也有被选择;
                if(isParentNodeSelected(indexList,index))
                { //上层已被选择,则取消当前选择;
                    this->selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
                    return ;  //返回,否则会被QTreeView::mousePressEvent(event)刷新;
                }
                else
                {//若下层已有选择,取消下层的所有选择;
                    this->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Current);
                    cancelChildrenNodeSelected(indexList,index);
                }
            }
        }
        else if(QApplication::keyboardModifiers()&Qt::ShiftModifier) //按住Shift键或与Ctrl一起按住;
        {
            QTreeView::mousePressEvent(event);//先更新按住shift键后的selectedIndexes;一般selectedIndexes()只记录前一次的,当前所做的操作不会更新到selectedIndexes中去;
            QModelIndexList indexList = this->selectedIndexes();
            resetSelectStateWhileShiftHolding(indexList);
            return ; //返回,否则会被QTreeView::mousePressEvent(event)刷新;
        }
    }

//    if(event->button()&Qt::LeftButton)
//    {
//        QModelIndex selectedIndex = this->currentIndex();
//        QModelIndex index = indexAt(event->pos());
//        m_selNode = treeModel->node(index);
//        if(m_selNode == treeModel->node(selectedIndex))
//        {
//           // treeModel->setData(index,m_selNode->data.name,Qt::EditRole);//useless...
//            this->edit(index,QAbstractItemView::SelectedClicked,event); //编辑状态;
//        }
//    }

    QTreeView::mousePressEvent(event);
}

void TreeView::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button()&Qt::LeftButton) //框选树节点;
    {
        QModelIndexList indexList = this->selectedIndexes();
        resetSelectStateWhileShiftHolding(indexList);
    }
    QTreeView::mouseReleaseEvent(event);
}

void TreeView::slotCustomContextMenu(const QPoint &pt)
{
    QPoint curPt = QCursor::pos();
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());
    if(m_selNode != treeModel->node(indexAt(curPt)))
    {
        switch(m_selNode->data.type)
        {
        case CONTAINER:
            m_popmenu->actions().at(0)->setVisible(true);
            m_popmenu->actions().at(1)->setVisible(true);
            m_popmenu->actions().at(2)->setVisible(true);
            m_popmenu->actions().at(3)->setVisible(true);
            break;
        case OBJECT:
            m_popmenu->actions().at(0)->setVisible(true);
            m_popmenu->actions().at(1)->setVisible(true);
            m_popmenu->actions().at(2)->setVisible(true);
            m_popmenu->actions().at(3)->setVisible(true);
            break;
        }
    }
    else  //在空白处;
    {
        m_popmenu->actions().at(0)->setVisible(true);
        m_popmenu->actions().at(1)->setVisible(true);
        m_popmenu->actions().at(2)->setVisible(false);
        m_popmenu->actions().at(3)->setVisible(false);
    }

    m_popmenu->exec(curPt);
}

void TreeView::slotCreateGroup()
{
    createNode("new Group",CONTAINER);
}

void TreeView::slotNewItem()
{
    createNode("new Object",OBJECT);
}

void TreeView::slotDeleteItem()
{
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());
    QModelIndexList indexList = this->selectedIndexes();
    if(indexList.isEmpty())
        return ;
    foreach(QModelIndex index,indexList)
    {
        TreeNode* node = treeModel->node(index);
        int row = node->parent()->index(node);
        treeModel->removeRows(row,1,treeModel->parent(index));
        treeModel->deleteTmpVect();
    }
}

void TreeView::slotRenameItem()
{
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());
    if(m_selNode && m_selNode != treeModel->root())
    {
        this->edit(this->currentIndex());
        return ;
    }
}

QModelIndex TreeView::getIndex(const QModelIndex& parentIndex, int& row) const
{
    if(!parentIndex.isValid())
        return QModelIndex();

    row--;
    if(row<=0)
        return parentIndex;

    QModelIndex resIndex = QModelIndex();
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());
    int nChildCount = treeModel->rowCount(parentIndex);
    if(nChildCount>0 && this->isExpanded(parentIndex))
    {

        for(int i=0;i<nChildCount;i++)
        {
            QModelIndex childIndex = treeModel->index(i,0,parentIndex) ;
            if(row>0)
                resIndex = getIndex(childIndex,row);
        }

    }

    return resIndex;
}

void TreeView::performDrag()
{
    QModelIndex curIndex = this->currentIndex();
    if(curIndex.isValid())
    {
        QMimeData* mimeData = new QMimeData;
        mimeData->setText(curIndex.data().toString());
        QDrag* drag = new QDrag(this);
        drag->setMimeData(mimeData);
        drag->setPixmap(QPixmap("qmls/pic/Qt.ico"));
        drag->exec(Qt::MoveAction);
    }
}

void TreeView::createNode(const QString &name, int type)
{
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());
    if(m_selNode != treeModel->root())
    {
        treeModel->clearTmpVect();
        treeModel->appendNodeToTmpVect(new TreeNode(TreeNodeData(name,type)));
        int row = -1;
        TreeNode* parentNode = m_selNode;
        if(m_selNode->data.type!=CONTAINER) //若非CONTAINER类型,则不能挂靠,只能插入;
        {
            parentNode = m_selNode->parent();
            row = parentNode->index(m_selNode)+1;
        }
        treeModel->insertRows(row,1,treeModel->index(parentNode));
        return ;
    }

    //在空白处右击时,直接添加;
    //if(m_selNode == treeModel->root())
    {
        treeModel->clearTmpVect();
        treeModel->appendNodeToTmpVect(new TreeNode(TreeNodeData(name,type)));
        treeModel->insertRows(-1,1,QModelIndex());
    }
}

bool TreeView::isParentNodeSelected(const QModelIndexList& selectedList,const QModelIndex &index)
{
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());

    if(!index.isValid() || selectedList.isEmpty())
        return false;

    QModelIndex parentIndex = treeModel->parent(index);
    while(parentIndex.isValid())
    {
        if(selectedList.contains(parentIndex))
            return true;
        parentIndex = treeModel->parent(parentIndex);
    }

    return false;
}

void TreeView::cancelChildrenNodeSelected(const QModelIndexList& selectedList,const QModelIndex &index)
{
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());

    if(!index.isValid() || selectedList.isEmpty())
        return ;

    TreeNode* node = treeModel->node(index);
    if(!node || node->childCount()<=0)
        return ;

    for(int i=0;i<node->childCount();i++)
    {
        QModelIndex chIndex = treeModel->index(node->child(i));
        if(chIndex.isValid() && selectedList.contains(chIndex))
        {
            this->selectionModel()->setCurrentIndex(chIndex, QItemSelectionModel::Deselect);
        }
        cancelChildrenNodeSelected(selectedList,chIndex);
    }
}

void TreeView::resetSelectStateWhileShiftHolding(const QModelIndexList &selectedList)
{ //若存在父子关系的,把所有的被选中的子节点取消选中,只留下枝上最顶端被选中的即可;

    if(selectedList.isEmpty())
        return ;

    //先找出最顶端的被选中的节点;
    QModelIndexList topestList;
    foreach(QModelIndex index,selectedList)
    {
        QModelIndex topestIndex = index;
        getTopestParentSelected(selectedList,index,topestIndex);
        if(!topestList.contains(topestIndex))
            topestList.append(topestIndex);
    }

    if(topestList.count() == selectedList.count()) //没有存在父子关系的,都是topestIndex;
        return ;

    //除了topestIndex,其它的都取消选中;
    foreach(QModelIndex index,selectedList)
    {
        if(!topestList.contains(index))
            this->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Deselect);
    }
}

void TreeView::getTopestParentSelected(const QModelIndexList &selectedList, const QModelIndex &index,QModelIndex& topestIndex)
{
    TreeModel* treeModel = qobject_cast<TreeModel*>(this->model());
    if(index.isValid())
    {
        if(selectedList.contains(index))
            topestIndex = index;
        getTopestParentSelected(selectedList,treeModel->parent(index),topestIndex);
    }
}

代码贴完了,由于粘贴后每行会多出一行空行,而代码量太多,不再一一整理了。在代码粘贴这一块,网易博客不太友好。
  评论这张
 
阅读(22)| 评论(0)
推荐

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017