Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Full drag and drop support in QTreeView
Forum Updated to NodeBB v4.3 + New Features

Full drag and drop support in QTreeView

Scheduled Pinned Locked Moved Solved General and Desktop
6 Posts 4 Posters 14.1k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    Patou355
    wrote on last edited by VRonin
    #1

    Hello all.

    I'm trying to implement a fully functional drag and drop in a TreeView/TreeModel tandem. I consider myself as a Qt beginner and I didn't write the pieces of code you'll see alone.

    First, here are the needs.

    • an inner drag-and-drop (not from another widget or an external application) ;
    • if the item A is dropped on the item B, A is moved as a child of B ;
    • the TreeView shows items A, B and C: if I drag A item between B and C, the order becomes B, A and C ;
    • the TreeModel is built from a std::vector<std::string>.

    If the line removeNode(oldNode); of the function TreeModel::dropMimeData is commented, the drop as the child works, excepted that it's not erased at the original position (of course). When uncommented, it crashes on drag-and-drop when I release the mouse button..

    The debugger says TreeNode *oldNode pointer is 0x0.

    If I change the mimeData function code into:

    QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
    {
        QMimeData *mimeData = new QMimeData;
        QByteArray data;
        QDataStream stream(&data, QIODevice::WriteOnly);
        QList<TreeNode *> nodes;
        foreach (const QModelIndex &index, indexes) {
            TreeNode *node = nodeForIndex(index);
            if (!nodes.contains(node))
                nodes << node;
        }
        stream << QCoreApplication::applicationPid();
        stream << nodes.count();
        foreach (TreeNode *node, nodes) {
            stream << reinterpret_cast<qlonglong>(node);
        }
        mimeData->setData(s_treeNodeMimeType, data);
        return mimeData;
    }
    

    there's no crash, but nothing happens on drag-and-drop

    In fact, the question is: is there an easy way to get the pointer to the old TreeNode in order to remove it. Easy should be understood as "few lines".

    Here's the complete source code of the subclassed QTreeModel:

    /*
        treemodel.cpp
    
        Provides a simple tree model to show how to create and use hierarchical
        models.
    */
    
    #include "TreeNode.h"
    #include "TreeModel.h"
    
    #include <QStringList>
    #include <QMimeData>
    #include <QIODevice>
    #include <QDataStream>
    
    TreeModel::TreeModel(const std::vector<std::string> &modalities, QObject *parent) : QAbstractItemModel(parent)
    {
    
        QStringList data;
    
        int i;
    
        for (i = 0 ; i < modalities.size() ; ++i)
            data.push_back(QString::fromStdString(modalities.at(i)));
    
        QList<QVariant> rootData;
        rootData << "Occurrences";
        m_rootNode = new TreeNode(rootData, 0);
        setupModelData(data, m_rootNode);
    }
    
    QVariant TreeModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
            return QVariant();
    
        if (role != Qt::DisplayRole)
            return QVariant();
    
        TreeNode *node = nodeForIndex(index);
        return node->data(index.column());
    }
    
    QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                   int role) const
    {
        if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
            return m_rootNode->data(section);
    
        return QVariant();
    }
    
    Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
    {
        Qt::ItemFlags flags = QAbstractItemModel::flags(index);
    
        if (index.isValid()) //teste si on est bien sur un item...
            return flags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
         else
            return flags | Qt::ItemIsDropEnabled;
    }
    
    Qt::DropActions TreeModel::supportedDropActions() const //supported actions when dropping
    {
        return Qt::CopyAction | Qt::MoveAction;
    }
    
    static const char s_treeNodeMimeType[] = "application/x-treenode";
    
    QStringList TreeModel::mimeTypes() const
    {
        return QAbstractItemModel::mimeTypes() << s_treeNodeMimeType;
    }
    
    // Filling datas for moved objects
    QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
    {
        QStringList list;
        foreach( const QModelIndex &index, indexes )
            list << data(index, Qt::DisplayRole).toString();
    
        QMimeData* mimeData = QAbstractItemModel::mimeData(indexes);
        mimeData->setText(list.join(", "));
        return mimeData;
    }
    
    //called function when object dropped
    bool TreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
    {
        if  (data->hasText()) {
            // on drop sur un item => le parent c'est l'item
            if (parent.isValid()) {
                const int lastRow = rowCount(parent);
    
                beginInsertRows(parent, lastRow, lastRow); // method to tell to the view that we will add a line (internal Qt stuff)
    
                //ajout de l'enfant
                TreeNode *parentNode = nodeForIndex(parent);
                QList<QVariant> columnData;
                columnData << data->text();
                parentNode->appendChild(new TreeNode(columnData, parentNode));
    
                QByteArray ddata = data->data(s_treeNodeMimeType);
                QDataStream stream(&ddata, QIODevice::ReadOnly);
                qint64 senderPid;
                stream >> senderPid;
                int count;
                stream >> count;
    
                qlonglong nodePtr;
                stream >> nodePtr;
                TreeNode *oldNode = reinterpret_cast<TreeNode *>(nodePtr);
    
                // Remove from old position
                removeNode(oldNode);
                endInsertRows();//calling the signals for updating the view (internal Qt cooking)
    
                return true;
            }
        }
    
        return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
    }
    
    TreeModel::~TreeModel()
    {
        delete m_rootNode;
    }
    
    int TreeModel::columnCount(const QModelIndex &parent) const
    {
        return nodeForIndex(parent)->columnCount();
    }
    
    TreeNode * TreeModel::nodeForIndex(const QModelIndex &index) const
    {
        if (!index.isValid())
            return m_rootNode;
        else
            return static_cast<TreeNode*>(index.internalPointer());
    }
    
    void TreeModel::removeNode(TreeNode *node)
    {
        const int row = node->row();
        QModelIndex idx = createIndex(row, 0, node);
        beginRemoveRows(idx.parent(), row, row);
        node->parentNode()->removeChild(row);
        endRemoveRows();
    }
    
    QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
    {
        if (!hasIndex(row, column, parent))
            return QModelIndex();
    
        TreeNode *parentNode = nodeForIndex(parent);
    
        TreeNode *childNode = parentNode->child(row);
        if (childNode)
            return createIndex(row, column, childNode);
        else
            return QModelIndex();
    }
    
    QModelIndex TreeModel::parent(const QModelIndex &index) const
    {
        TreeNode *childNode = nodeForIndex(index);
        if (childNode == m_rootNode)
            return QModelIndex();
    
        TreeNode *parentNode = childNode->parentNode();
        if (parentNode == m_rootNode)
            return QModelIndex();
    
        return createIndex(parentNode->row(), 0, parentNode);
    }
    
    int TreeModel::rowCount(const QModelIndex &parent) const
    {
        if (parent.column() > 0)
            return 0;
    
        TreeNode *parentNode = nodeForIndex(parent);
        return parentNode->childCount();
    }
    
    void TreeModel::setupModelData(const QStringList &lines, TreeNode *parent)
    {
        QList<TreeNode*> parents;
        QList<int> indentations;
        parents << parent;
        indentations << 0;
    
        int number = 0;
    
        while (number < lines.count()) {
            int position = 0;
            while (position < lines[number].length()) {
                if (lines[number].mid(position, 1) != " ")
                    break;
                position++;
            }
    
            QString lineData = lines[number].mid(position).trimmed();
    
            if (!lineData.isEmpty()) {
                // Read the column data from the rest of the line.
                QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
                QList<QVariant> columnData;
                for (int column = 0; column < columnStrings.count(); ++column)
                    columnData << columnStrings[column];
    
                if (position > indentations.last()) {
                    // The last child of the current parent is now the new parent
                    // unless the current parent has no children.
    
                    if (parents.last()->childCount() > 0) {
                        parents << parents.last()->child(parents.last()->childCount()-1);
                        indentations << position;
                    }
                } else {
                    while (position < indentations.last() && parents.count() > 0) {
                        parents.pop_back();
                        indentations.pop_back();
                    }
                }
    
                // Append a new node to the current parent's list of children.
                parents.last()->appendChild(new TreeNode(columnData, parents.last()));
            }
    
            ++number;
        }
    }
    
    

    The header file :

    #ifndef TREEMODEL_H
    #define TREEMODEL_H
    
    #include <QAbstractItemModel>
    #include <QModelIndex>
    #include <QVariant>
    
    class TreeNode;
    
    class TreeModel : public QAbstractItemModel
    {
        Q_OBJECT
    
    public:
        explicit TreeModel(const std::vector<std::__cxx11::string> &modalities, QObject *parent = 0);
        ~TreeModel();
    
        QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
        QVariant headerData(int section, Qt::Orientation orientation,
                            int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
        QModelIndex index(int row, int column,
                          const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
        QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
        int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
        int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
        Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
        Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE;
        QStringList mimeTypes() const Q_DECL_OVERRIDE;
        QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE;
        bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex & parent);
        void removeNode(TreeNode *node);
    
    private:
        TreeNode * nodeForIndex(const QModelIndex &index) const;
        void setupModelData(const QStringList &lines, TreeNode *parent);
    
        TreeNode *m_rootNode;
    };
    
    #endif // TREEMODEL_H
    
    
    P 1 Reply Last reply
    0
    • P Patou355

      Hello all.

      I'm trying to implement a fully functional drag and drop in a TreeView/TreeModel tandem. I consider myself as a Qt beginner and I didn't write the pieces of code you'll see alone.

      First, here are the needs.

      • an inner drag-and-drop (not from another widget or an external application) ;
      • if the item A is dropped on the item B, A is moved as a child of B ;
      • the TreeView shows items A, B and C: if I drag A item between B and C, the order becomes B, A and C ;
      • the TreeModel is built from a std::vector<std::string>.

      If the line removeNode(oldNode); of the function TreeModel::dropMimeData is commented, the drop as the child works, excepted that it's not erased at the original position (of course). When uncommented, it crashes on drag-and-drop when I release the mouse button..

      The debugger says TreeNode *oldNode pointer is 0x0.

      If I change the mimeData function code into:

      QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
      {
          QMimeData *mimeData = new QMimeData;
          QByteArray data;
          QDataStream stream(&data, QIODevice::WriteOnly);
          QList<TreeNode *> nodes;
          foreach (const QModelIndex &index, indexes) {
              TreeNode *node = nodeForIndex(index);
              if (!nodes.contains(node))
                  nodes << node;
          }
          stream << QCoreApplication::applicationPid();
          stream << nodes.count();
          foreach (TreeNode *node, nodes) {
              stream << reinterpret_cast<qlonglong>(node);
          }
          mimeData->setData(s_treeNodeMimeType, data);
          return mimeData;
      }
      

      there's no crash, but nothing happens on drag-and-drop

      In fact, the question is: is there an easy way to get the pointer to the old TreeNode in order to remove it. Easy should be understood as "few lines".

      Here's the complete source code of the subclassed QTreeModel:

      /*
          treemodel.cpp
      
          Provides a simple tree model to show how to create and use hierarchical
          models.
      */
      
      #include "TreeNode.h"
      #include "TreeModel.h"
      
      #include <QStringList>
      #include <QMimeData>
      #include <QIODevice>
      #include <QDataStream>
      
      TreeModel::TreeModel(const std::vector<std::string> &modalities, QObject *parent) : QAbstractItemModel(parent)
      {
      
          QStringList data;
      
          int i;
      
          for (i = 0 ; i < modalities.size() ; ++i)
              data.push_back(QString::fromStdString(modalities.at(i)));
      
          QList<QVariant> rootData;
          rootData << "Occurrences";
          m_rootNode = new TreeNode(rootData, 0);
          setupModelData(data, m_rootNode);
      }
      
      QVariant TreeModel::data(const QModelIndex &index, int role) const
      {
          if (!index.isValid())
              return QVariant();
      
          if (role != Qt::DisplayRole)
              return QVariant();
      
          TreeNode *node = nodeForIndex(index);
          return node->data(index.column());
      }
      
      QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                     int role) const
      {
          if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
              return m_rootNode->data(section);
      
          return QVariant();
      }
      
      Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
      {
          Qt::ItemFlags flags = QAbstractItemModel::flags(index);
      
          if (index.isValid()) //teste si on est bien sur un item...
              return flags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
           else
              return flags | Qt::ItemIsDropEnabled;
      }
      
      Qt::DropActions TreeModel::supportedDropActions() const //supported actions when dropping
      {
          return Qt::CopyAction | Qt::MoveAction;
      }
      
      static const char s_treeNodeMimeType[] = "application/x-treenode";
      
      QStringList TreeModel::mimeTypes() const
      {
          return QAbstractItemModel::mimeTypes() << s_treeNodeMimeType;
      }
      
      // Filling datas for moved objects
      QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
      {
          QStringList list;
          foreach( const QModelIndex &index, indexes )
              list << data(index, Qt::DisplayRole).toString();
      
          QMimeData* mimeData = QAbstractItemModel::mimeData(indexes);
          mimeData->setText(list.join(", "));
          return mimeData;
      }
      
      //called function when object dropped
      bool TreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
      {
          if  (data->hasText()) {
              // on drop sur un item => le parent c'est l'item
              if (parent.isValid()) {
                  const int lastRow = rowCount(parent);
      
                  beginInsertRows(parent, lastRow, lastRow); // method to tell to the view that we will add a line (internal Qt stuff)
      
                  //ajout de l'enfant
                  TreeNode *parentNode = nodeForIndex(parent);
                  QList<QVariant> columnData;
                  columnData << data->text();
                  parentNode->appendChild(new TreeNode(columnData, parentNode));
      
                  QByteArray ddata = data->data(s_treeNodeMimeType);
                  QDataStream stream(&ddata, QIODevice::ReadOnly);
                  qint64 senderPid;
                  stream >> senderPid;
                  int count;
                  stream >> count;
      
                  qlonglong nodePtr;
                  stream >> nodePtr;
                  TreeNode *oldNode = reinterpret_cast<TreeNode *>(nodePtr);
      
                  // Remove from old position
                  removeNode(oldNode);
                  endInsertRows();//calling the signals for updating the view (internal Qt cooking)
      
                  return true;
              }
          }
      
          return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
      }
      
      TreeModel::~TreeModel()
      {
          delete m_rootNode;
      }
      
      int TreeModel::columnCount(const QModelIndex &parent) const
      {
          return nodeForIndex(parent)->columnCount();
      }
      
      TreeNode * TreeModel::nodeForIndex(const QModelIndex &index) const
      {
          if (!index.isValid())
              return m_rootNode;
          else
              return static_cast<TreeNode*>(index.internalPointer());
      }
      
      void TreeModel::removeNode(TreeNode *node)
      {
          const int row = node->row();
          QModelIndex idx = createIndex(row, 0, node);
          beginRemoveRows(idx.parent(), row, row);
          node->parentNode()->removeChild(row);
          endRemoveRows();
      }
      
      QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
      {
          if (!hasIndex(row, column, parent))
              return QModelIndex();
      
          TreeNode *parentNode = nodeForIndex(parent);
      
          TreeNode *childNode = parentNode->child(row);
          if (childNode)
              return createIndex(row, column, childNode);
          else
              return QModelIndex();
      }
      
      QModelIndex TreeModel::parent(const QModelIndex &index) const
      {
          TreeNode *childNode = nodeForIndex(index);
          if (childNode == m_rootNode)
              return QModelIndex();
      
          TreeNode *parentNode = childNode->parentNode();
          if (parentNode == m_rootNode)
              return QModelIndex();
      
          return createIndex(parentNode->row(), 0, parentNode);
      }
      
      int TreeModel::rowCount(const QModelIndex &parent) const
      {
          if (parent.column() > 0)
              return 0;
      
          TreeNode *parentNode = nodeForIndex(parent);
          return parentNode->childCount();
      }
      
      void TreeModel::setupModelData(const QStringList &lines, TreeNode *parent)
      {
          QList<TreeNode*> parents;
          QList<int> indentations;
          parents << parent;
          indentations << 0;
      
          int number = 0;
      
          while (number < lines.count()) {
              int position = 0;
              while (position < lines[number].length()) {
                  if (lines[number].mid(position, 1) != " ")
                      break;
                  position++;
              }
      
              QString lineData = lines[number].mid(position).trimmed();
      
              if (!lineData.isEmpty()) {
                  // Read the column data from the rest of the line.
                  QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
                  QList<QVariant> columnData;
                  for (int column = 0; column < columnStrings.count(); ++column)
                      columnData << columnStrings[column];
      
                  if (position > indentations.last()) {
                      // The last child of the current parent is now the new parent
                      // unless the current parent has no children.
      
                      if (parents.last()->childCount() > 0) {
                          parents << parents.last()->child(parents.last()->childCount()-1);
                          indentations << position;
                      }
                  } else {
                      while (position < indentations.last() && parents.count() > 0) {
                          parents.pop_back();
                          indentations.pop_back();
                      }
                  }
      
                  // Append a new node to the current parent's list of children.
                  parents.last()->appendChild(new TreeNode(columnData, parents.last()));
              }
      
              ++number;
          }
      }
      
      

      The header file :

      #ifndef TREEMODEL_H
      #define TREEMODEL_H
      
      #include <QAbstractItemModel>
      #include <QModelIndex>
      #include <QVariant>
      
      class TreeNode;
      
      class TreeModel : public QAbstractItemModel
      {
          Q_OBJECT
      
      public:
          explicit TreeModel(const std::vector<std::__cxx11::string> &modalities, QObject *parent = 0);
          ~TreeModel();
      
          QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
          QVariant headerData(int section, Qt::Orientation orientation,
                              int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
          QModelIndex index(int row, int column,
                            const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
          QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
          int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
          int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
          Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
          Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE;
          QStringList mimeTypes() const Q_DECL_OVERRIDE;
          QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE;
          bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex & parent);
          void removeNode(TreeNode *node);
      
      private:
          TreeNode * nodeForIndex(const QModelIndex &index) const;
          void setupModelData(const QStringList &lines, TreeNode *parent);
      
          TreeNode *m_rootNode;
      };
      
      #endif // TREEMODEL_H
      
      
      P Offline
      P Offline
      pvt.peter
      wrote on last edited by
      #2

      Hi @Patou355,

      Is there any generic solution to full drag and drop support in QTreeView?

      1 Reply Last reply
      0
      • P Offline
        P Offline
        Patou355
        wrote on last edited by
        #3

        In fact, here's the solution I used...
        It's a very long, hard level source code and 3 subclass to define such a banal behavior... But now it works.

        TreeModel header:

        #ifndef TREEMODEL_H
        #define TREEMODEL_H
        
        #include <QAbstractItemModel>
        #include <QModelIndex>
        #include <QVariant>
        
        class TreeNode;
        
        class TreeModel : public QAbstractItemModel
        {
            Q_OBJECT
        
        public:
            explicit TreeModel(const QStringList &modalities, QObject *parent = 0);
            ~TreeModel();
        
            QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
            QVariant headerData(int section, Qt::Orientation orientation,
                                int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
            QModelIndex index(int row, int column,
                              const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
            QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
            int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
            int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
            Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
            Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE;
            Qt::DropActions supportedDragActions() const Q_DECL_OVERRIDE;
            QStringList mimeTypes() const Q_DECL_OVERRIDE;
            QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE;
            bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex & parent);
        
        private:
            TreeNode * nodeForIndex(const QModelIndex &index) const;
            void removeNode(TreeNode *node);
            void setupModelData(const QStringList &lines, TreeNode *parent);
        
            TreeNode *m_rootNode;
        };
        
        #endif // TREEMODEL_H
        

        TreeModel source:

        #include "TreeNode.h"
        #include "TreeModel.h"
        
        #include <QCoreApplication>
        #include <QStringList>
        #include <QMimeData>
        #include <QIODevice>
        #include <QDataStream>
        
        TreeModel::TreeModel(const QStringList &modalities, QObject *parent)
            : QAbstractItemModel(parent)
        {
        
            QStringList data = modalities;
        
        //    for (int i = 1 ; i < modalities.size() ; ++i)
        //        data.push_back(QString::fromStdString(modalities.at(i)));
        
            QList<QVariant> rootData;
            rootData << "Levels";
            m_rootNode = new TreeNode(rootData, 0);
            setupModelData(data, m_rootNode);
        }
        
        TreeModel::~TreeModel()
        {
            delete m_rootNode;
        }
        
        int TreeModel::columnCount(const QModelIndex &parent) const
        {
            return nodeForIndex(parent)->columnCount();
        }
        
        static const char s_treeNodeMimeType[] = "application/x-treenode";
        
        //returns the mime type
        QStringList TreeModel::mimeTypes() const
        {
            return QStringList() << s_treeNodeMimeType;
        }
        
        //receives a list of model indexes list
        QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
        {
            QMimeData *mimeData = new QMimeData;
            QByteArray data; //a kind of RAW format for datas
        
            //QDataStream is independant on the OS or proc architecture
            //serialization of C++'s basic data types, like char, short, int, char *, etc.
            //Serialization of more complex data is accomplished
            //by breaking up the data into primitive units.
            QDataStream stream(&data, QIODevice::WriteOnly);
            QList<TreeNode *> nodes;
        
            //
            foreach (const QModelIndex &index, indexes) {
                TreeNode *node = nodeForIndex(index);
                if (!nodes.contains(node))
                    nodes << node;
            }
            stream << QCoreApplication::applicationPid();
            stream << nodes.count();
            foreach (TreeNode *node, nodes) {
                stream << reinterpret_cast<qlonglong>(node);
            }
            mimeData->setData(s_treeNodeMimeType, data);
            return mimeData;
        }
        
        bool TreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent)
        {
            Q_ASSERT(action == Qt::MoveAction);
            Q_UNUSED(column);
            //test if the data type is the good one
            if (!mimeData->hasFormat(s_treeNodeMimeType)) {
                return false;
            }
            QByteArray data = mimeData->data(s_treeNodeMimeType);
            QDataStream stream(&data, QIODevice::ReadOnly);
            qint64 senderPid;
            stream >> senderPid;
            if (senderPid != QCoreApplication::applicationPid()) {
                // Let's not cast pointers that come from another process...
                return false;
            }
            TreeNode *parentNode = nodeForIndex(parent);
            Q_ASSERT(parentNode);
            int count;
            stream >> count;
            if (row == -1) {
                // valid index means: drop onto item. I chose that this should insert
                // a child item, because this is the only way to create the first child of an item...
                // This explains why Qt calls it parent: unless you just support replacing, this
                // is really the future parent of the dropped items.
                if (parent.isValid())
                    row = 0;
                else
                    // invalid index means: append at bottom, after last toplevel
                    row = rowCount(parent);
            }
            for (int i = 0; i < count; ++i) {
                // Decode data from the QMimeData
                qlonglong nodePtr;
                stream >> nodePtr;
                TreeNode *node = reinterpret_cast<TreeNode *>(nodePtr);
        
                // Adjust destination row for the case of moving an item
                // within the same parent, to a position further down.
                // Its own removal will reduce the final row number by one.
                if (node->row() < row && parentNode == node->parentNode())
                    --row;
        
                // Remove from old position
                removeNode(node);
        
                // Insert at new position
                //qDebug() << "Inserting into" << parent << row;
                beginInsertRows(parent, row, row);
                parentNode->insertChild(row, node);
                endInsertRows();
                ++row;
            }
            return true;
        }
        
        Qt::DropActions TreeModel::supportedDropActions() const
        {
            return Qt::MoveAction;
        }
        
        Qt::DropActions TreeModel::supportedDragActions() const
        {
            return Qt::MoveAction;
        }
        
        QVariant TreeModel::data(const QModelIndex &index, int role) const
        {
            if (!index.isValid())
                return QVariant();
        
            if (role != Qt::DisplayRole)
                return QVariant();
        
            TreeNode *node = nodeForIndex(index);
            return node->data(index.column());
        }
        
        Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
        {
            if (!index.isValid())
                return Qt::ItemIsDropEnabled;
        
            return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
        }
        
        QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                       int role) const
        {
            if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
                return m_rootNode->data(section);
        
            return QVariant();
        }
        
        //returns a pointer to the "index"
        TreeNode * TreeModel::nodeForIndex(const QModelIndex &index) const
        {
            if (!index.isValid())
                return m_rootNode;
            else
                return static_cast<TreeNode*>(index.internalPointer());
        }
        
        void TreeModel::removeNode(TreeNode *node)
        {
            const int row = node->row();
            QModelIndex idx = createIndex(row, 0, node);
            beginRemoveRows(idx.parent(), row, row);
            node->parentNode()->removeChild(row);
            endRemoveRows();
        }
        
        QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
        {
            if (!hasIndex(row, column, parent))
                return QModelIndex();
        
            TreeNode *parentNode = nodeForIndex(parent);
        
            TreeNode *childNode = parentNode->child(row);
            if (childNode)
                return createIndex(row, column, childNode);
            else
                return QModelIndex();
        }
        
        QModelIndex TreeModel::parent(const QModelIndex &index) const
        {
            TreeNode *childNode = nodeForIndex(index);
            if (childNode == m_rootNode)
                return QModelIndex();
        
            TreeNode *parentNode = childNode->parentNode();
            if (parentNode == m_rootNode)
                return QModelIndex();
        
            return createIndex(parentNode->row(), 0, parentNode);
        }
        
        int TreeModel::rowCount(const QModelIndex &parent) const
        {
            if (parent.column() > 0)
                return 0;
        
            TreeNode *parentNode = nodeForIndex(parent);
            return parentNode->childCount();
        }
        
        void TreeModel::setupModelData(const QStringList &lines, TreeNode *parent)
        {
            QList<TreeNode*> parents;
            QList<int> indentations;
            parents << parent;
            indentations << 0;
        
            int number = 0;
        
            while (number < lines.count()) {
                int position = 0;
                while (position < lines[number].length()) {
                    if (lines[number].mid(position, 1) != " ")
                        break;
                    position++;
                }
        
                QString lineData = lines[number].mid(position).trimmed();
        
                if (!lineData.isEmpty()) {
                    // Read the column data from the rest of the line.
                    QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
                    QList<QVariant> columnData;
                    for (int column = 0; column < columnStrings.count(); ++column)
                        columnData << columnStrings[column];
        
                    if (position > indentations.last()) {
                        // The last child of the current parent is now the new parent
                        // unless the current parent has no children.
        
                        if (parents.last()->childCount() > 0) {
                            parents << parents.last()->child(parents.last()->childCount()-1);
                            indentations << position;
                        }
                    } else {
                        while (position < indentations.last() && parents.count() > 0) {
                            parents.pop_back();
                            indentations.pop_back();
                        }
                    }
        
                    // Append a new node to the current parent's list of children.
                    parents.last()->appendChild(new TreeNode(columnData, parents.last()));
                }
        
                ++number;
            }
        }
        

        And the TreeView (nothing special).
        Header:

        #ifndef TREEVIEW_H
        #define TREEVIEW_H
        
        #include <QObject>
        #include <QTreeView>
        #include "TreeModel.h"
        
        class TreeView : public QTreeView
        {
        public:
            TreeView(TreeModel *model);
            TreeView();
            void setModel(TreeModel *model);
        };
        
        #endif // TREEVIEW_H
        

        Source:

        #include "TreeView.h"
        #include "TreeModel.h"
        #include <QHeaderView>
        
        TreeView::TreeView(TreeModel *model)
        {
            setModel(model);
            TreeView();
        }
        
        TreeView::TreeView()
        {
            connect(this, &QTreeView::pressed, this, &QTreeView::expandAll);
            setDragEnabled(true);
            setAcceptDrops(true);
            resizeColumnToContents(0);
            resize(400, 500);
            setSelectionMode(QAbstractItemView::SingleSelection);
            expandAll();
            connect(this, &QTreeView::pressed, this, &QTreeView::expandAll);
            header()->setVisible(false);
            setIndentation(5);
            setEditTriggers(QAbstractItemView::EditTriggers());
        }
        
        void TreeView::setModel(TreeModel *model)
        {
            //Kick what's already selected (http://doc.qt.io/qt-4.8/qabstractitemview.html#setModel)
            QItemSelectionModel *m = selectionModel();
            delete m;
        }
        

        And the TreeNode:

        #ifndef TREENODE_H
        #define TREENODE_H
        
        #include <QList>
        #include <QVariant>
        
        class TreeNode
        {
        public:
            explicit TreeNode(const QList<QVariant> &data, TreeNode *parentNode);
            ~TreeNode();
        
            void appendChild(TreeNode *child);
            void removeChild(int row);
        
            TreeNode *child(int row) const;
            int childCount() const;
            int columnCount() const;
            QVariant data(int column) const;
            int row() const;
            TreeNode *parentNode() const;
            void insertChild(int pos, TreeNode *child);
        
        private:
            QList<TreeNode*> m_childNodes;
            QList<QVariant> m_nodeData;
            TreeNode *m_parentNode;
        };
        
        #endif // TREENODE_H
        

        source:

        #include "TreeNode.h"
        
        #include <QStringList>
        
        TreeNode::TreeNode(const QList<QVariant> &data, TreeNode *parent)
            : m_nodeData(data), m_parentNode(parent)
        {
        }
        
        TreeNode::~TreeNode()
        {
            qDeleteAll(m_childNodes);
        }
        
        void TreeNode::appendChild(TreeNode *node)
        {
            m_childNodes.append(node);
        }
        
        void TreeNode::removeChild(int row)
        {
            m_childNodes.removeAt(row);
        }
        
        TreeNode *TreeNode::child(int row) const
        {
            return m_childNodes.value(row);
        }
        
        int TreeNode::childCount() const
        {
            return m_childNodes.count();
        }
        
        int TreeNode::columnCount() const
        {
            return m_nodeData.count();
        }
        
        QVariant TreeNode::data(int column) const
        {
            return m_nodeData.value(column);
        }
        
        TreeNode *TreeNode::parentNode() const
        {
            return m_parentNode;
        }
        
        int TreeNode::row() const
        {
            if (m_parentNode)
                return m_parentNode->m_childNodes.indexOf(const_cast<TreeNode*>(this));
        
            return 0;
        }
        
        void TreeNode::insertChild(int pos, TreeNode *child)
        {
            m_childNodes.insert(pos, child);
            child->m_parentNode = this;
        }
        
        P P 2 Replies Last reply
        3
        • P Patou355

          In fact, here's the solution I used...
          It's a very long, hard level source code and 3 subclass to define such a banal behavior... But now it works.

          TreeModel header:

          #ifndef TREEMODEL_H
          #define TREEMODEL_H
          
          #include <QAbstractItemModel>
          #include <QModelIndex>
          #include <QVariant>
          
          class TreeNode;
          
          class TreeModel : public QAbstractItemModel
          {
              Q_OBJECT
          
          public:
              explicit TreeModel(const QStringList &modalities, QObject *parent = 0);
              ~TreeModel();
          
              QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
              QVariant headerData(int section, Qt::Orientation orientation,
                                  int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
              QModelIndex index(int row, int column,
                                const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
              QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
              int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
              int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
              Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
              Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE;
              Qt::DropActions supportedDragActions() const Q_DECL_OVERRIDE;
              QStringList mimeTypes() const Q_DECL_OVERRIDE;
              QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE;
              bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex & parent);
          
          private:
              TreeNode * nodeForIndex(const QModelIndex &index) const;
              void removeNode(TreeNode *node);
              void setupModelData(const QStringList &lines, TreeNode *parent);
          
              TreeNode *m_rootNode;
          };
          
          #endif // TREEMODEL_H
          

          TreeModel source:

          #include "TreeNode.h"
          #include "TreeModel.h"
          
          #include <QCoreApplication>
          #include <QStringList>
          #include <QMimeData>
          #include <QIODevice>
          #include <QDataStream>
          
          TreeModel::TreeModel(const QStringList &modalities, QObject *parent)
              : QAbstractItemModel(parent)
          {
          
              QStringList data = modalities;
          
          //    for (int i = 1 ; i < modalities.size() ; ++i)
          //        data.push_back(QString::fromStdString(modalities.at(i)));
          
              QList<QVariant> rootData;
              rootData << "Levels";
              m_rootNode = new TreeNode(rootData, 0);
              setupModelData(data, m_rootNode);
          }
          
          TreeModel::~TreeModel()
          {
              delete m_rootNode;
          }
          
          int TreeModel::columnCount(const QModelIndex &parent) const
          {
              return nodeForIndex(parent)->columnCount();
          }
          
          static const char s_treeNodeMimeType[] = "application/x-treenode";
          
          //returns the mime type
          QStringList TreeModel::mimeTypes() const
          {
              return QStringList() << s_treeNodeMimeType;
          }
          
          //receives a list of model indexes list
          QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
          {
              QMimeData *mimeData = new QMimeData;
              QByteArray data; //a kind of RAW format for datas
          
              //QDataStream is independant on the OS or proc architecture
              //serialization of C++'s basic data types, like char, short, int, char *, etc.
              //Serialization of more complex data is accomplished
              //by breaking up the data into primitive units.
              QDataStream stream(&data, QIODevice::WriteOnly);
              QList<TreeNode *> nodes;
          
              //
              foreach (const QModelIndex &index, indexes) {
                  TreeNode *node = nodeForIndex(index);
                  if (!nodes.contains(node))
                      nodes << node;
              }
              stream << QCoreApplication::applicationPid();
              stream << nodes.count();
              foreach (TreeNode *node, nodes) {
                  stream << reinterpret_cast<qlonglong>(node);
              }
              mimeData->setData(s_treeNodeMimeType, data);
              return mimeData;
          }
          
          bool TreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent)
          {
              Q_ASSERT(action == Qt::MoveAction);
              Q_UNUSED(column);
              //test if the data type is the good one
              if (!mimeData->hasFormat(s_treeNodeMimeType)) {
                  return false;
              }
              QByteArray data = mimeData->data(s_treeNodeMimeType);
              QDataStream stream(&data, QIODevice::ReadOnly);
              qint64 senderPid;
              stream >> senderPid;
              if (senderPid != QCoreApplication::applicationPid()) {
                  // Let's not cast pointers that come from another process...
                  return false;
              }
              TreeNode *parentNode = nodeForIndex(parent);
              Q_ASSERT(parentNode);
              int count;
              stream >> count;
              if (row == -1) {
                  // valid index means: drop onto item. I chose that this should insert
                  // a child item, because this is the only way to create the first child of an item...
                  // This explains why Qt calls it parent: unless you just support replacing, this
                  // is really the future parent of the dropped items.
                  if (parent.isValid())
                      row = 0;
                  else
                      // invalid index means: append at bottom, after last toplevel
                      row = rowCount(parent);
              }
              for (int i = 0; i < count; ++i) {
                  // Decode data from the QMimeData
                  qlonglong nodePtr;
                  stream >> nodePtr;
                  TreeNode *node = reinterpret_cast<TreeNode *>(nodePtr);
          
                  // Adjust destination row for the case of moving an item
                  // within the same parent, to a position further down.
                  // Its own removal will reduce the final row number by one.
                  if (node->row() < row && parentNode == node->parentNode())
                      --row;
          
                  // Remove from old position
                  removeNode(node);
          
                  // Insert at new position
                  //qDebug() << "Inserting into" << parent << row;
                  beginInsertRows(parent, row, row);
                  parentNode->insertChild(row, node);
                  endInsertRows();
                  ++row;
              }
              return true;
          }
          
          Qt::DropActions TreeModel::supportedDropActions() const
          {
              return Qt::MoveAction;
          }
          
          Qt::DropActions TreeModel::supportedDragActions() const
          {
              return Qt::MoveAction;
          }
          
          QVariant TreeModel::data(const QModelIndex &index, int role) const
          {
              if (!index.isValid())
                  return QVariant();
          
              if (role != Qt::DisplayRole)
                  return QVariant();
          
              TreeNode *node = nodeForIndex(index);
              return node->data(index.column());
          }
          
          Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
          {
              if (!index.isValid())
                  return Qt::ItemIsDropEnabled;
          
              return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
          }
          
          QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                         int role) const
          {
              if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
                  return m_rootNode->data(section);
          
              return QVariant();
          }
          
          //returns a pointer to the "index"
          TreeNode * TreeModel::nodeForIndex(const QModelIndex &index) const
          {
              if (!index.isValid())
                  return m_rootNode;
              else
                  return static_cast<TreeNode*>(index.internalPointer());
          }
          
          void TreeModel::removeNode(TreeNode *node)
          {
              const int row = node->row();
              QModelIndex idx = createIndex(row, 0, node);
              beginRemoveRows(idx.parent(), row, row);
              node->parentNode()->removeChild(row);
              endRemoveRows();
          }
          
          QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
          {
              if (!hasIndex(row, column, parent))
                  return QModelIndex();
          
              TreeNode *parentNode = nodeForIndex(parent);
          
              TreeNode *childNode = parentNode->child(row);
              if (childNode)
                  return createIndex(row, column, childNode);
              else
                  return QModelIndex();
          }
          
          QModelIndex TreeModel::parent(const QModelIndex &index) const
          {
              TreeNode *childNode = nodeForIndex(index);
              if (childNode == m_rootNode)
                  return QModelIndex();
          
              TreeNode *parentNode = childNode->parentNode();
              if (parentNode == m_rootNode)
                  return QModelIndex();
          
              return createIndex(parentNode->row(), 0, parentNode);
          }
          
          int TreeModel::rowCount(const QModelIndex &parent) const
          {
              if (parent.column() > 0)
                  return 0;
          
              TreeNode *parentNode = nodeForIndex(parent);
              return parentNode->childCount();
          }
          
          void TreeModel::setupModelData(const QStringList &lines, TreeNode *parent)
          {
              QList<TreeNode*> parents;
              QList<int> indentations;
              parents << parent;
              indentations << 0;
          
              int number = 0;
          
              while (number < lines.count()) {
                  int position = 0;
                  while (position < lines[number].length()) {
                      if (lines[number].mid(position, 1) != " ")
                          break;
                      position++;
                  }
          
                  QString lineData = lines[number].mid(position).trimmed();
          
                  if (!lineData.isEmpty()) {
                      // Read the column data from the rest of the line.
                      QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
                      QList<QVariant> columnData;
                      for (int column = 0; column < columnStrings.count(); ++column)
                          columnData << columnStrings[column];
          
                      if (position > indentations.last()) {
                          // The last child of the current parent is now the new parent
                          // unless the current parent has no children.
          
                          if (parents.last()->childCount() > 0) {
                              parents << parents.last()->child(parents.last()->childCount()-1);
                              indentations << position;
                          }
                      } else {
                          while (position < indentations.last() && parents.count() > 0) {
                              parents.pop_back();
                              indentations.pop_back();
                          }
                      }
          
                      // Append a new node to the current parent's list of children.
                      parents.last()->appendChild(new TreeNode(columnData, parents.last()));
                  }
          
                  ++number;
              }
          }
          

          And the TreeView (nothing special).
          Header:

          #ifndef TREEVIEW_H
          #define TREEVIEW_H
          
          #include <QObject>
          #include <QTreeView>
          #include "TreeModel.h"
          
          class TreeView : public QTreeView
          {
          public:
              TreeView(TreeModel *model);
              TreeView();
              void setModel(TreeModel *model);
          };
          
          #endif // TREEVIEW_H
          

          Source:

          #include "TreeView.h"
          #include "TreeModel.h"
          #include <QHeaderView>
          
          TreeView::TreeView(TreeModel *model)
          {
              setModel(model);
              TreeView();
          }
          
          TreeView::TreeView()
          {
              connect(this, &QTreeView::pressed, this, &QTreeView::expandAll);
              setDragEnabled(true);
              setAcceptDrops(true);
              resizeColumnToContents(0);
              resize(400, 500);
              setSelectionMode(QAbstractItemView::SingleSelection);
              expandAll();
              connect(this, &QTreeView::pressed, this, &QTreeView::expandAll);
              header()->setVisible(false);
              setIndentation(5);
              setEditTriggers(QAbstractItemView::EditTriggers());
          }
          
          void TreeView::setModel(TreeModel *model)
          {
              //Kick what's already selected (http://doc.qt.io/qt-4.8/qabstractitemview.html#setModel)
              QItemSelectionModel *m = selectionModel();
              delete m;
          }
          

          And the TreeNode:

          #ifndef TREENODE_H
          #define TREENODE_H
          
          #include <QList>
          #include <QVariant>
          
          class TreeNode
          {
          public:
              explicit TreeNode(const QList<QVariant> &data, TreeNode *parentNode);
              ~TreeNode();
          
              void appendChild(TreeNode *child);
              void removeChild(int row);
          
              TreeNode *child(int row) const;
              int childCount() const;
              int columnCount() const;
              QVariant data(int column) const;
              int row() const;
              TreeNode *parentNode() const;
              void insertChild(int pos, TreeNode *child);
          
          private:
              QList<TreeNode*> m_childNodes;
              QList<QVariant> m_nodeData;
              TreeNode *m_parentNode;
          };
          
          #endif // TREENODE_H
          

          source:

          #include "TreeNode.h"
          
          #include <QStringList>
          
          TreeNode::TreeNode(const QList<QVariant> &data, TreeNode *parent)
              : m_nodeData(data), m_parentNode(parent)
          {
          }
          
          TreeNode::~TreeNode()
          {
              qDeleteAll(m_childNodes);
          }
          
          void TreeNode::appendChild(TreeNode *node)
          {
              m_childNodes.append(node);
          }
          
          void TreeNode::removeChild(int row)
          {
              m_childNodes.removeAt(row);
          }
          
          TreeNode *TreeNode::child(int row) const
          {
              return m_childNodes.value(row);
          }
          
          int TreeNode::childCount() const
          {
              return m_childNodes.count();
          }
          
          int TreeNode::columnCount() const
          {
              return m_nodeData.count();
          }
          
          QVariant TreeNode::data(int column) const
          {
              return m_nodeData.value(column);
          }
          
          TreeNode *TreeNode::parentNode() const
          {
              return m_parentNode;
          }
          
          int TreeNode::row() const
          {
              if (m_parentNode)
                  return m_parentNode->m_childNodes.indexOf(const_cast<TreeNode*>(this));
          
              return 0;
          }
          
          void TreeNode::insertChild(int pos, TreeNode *child)
          {
              m_childNodes.insert(pos, child);
              child->m_parentNode = this;
          }
          
          P Offline
          P Offline
          pvt.peter
          wrote on last edited by
          #4

          Hi @Patou355,

          Thanks for your code, it works well.

          1 Reply Last reply
          0
          • P Patou355

            In fact, here's the solution I used...
            It's a very long, hard level source code and 3 subclass to define such a banal behavior... But now it works.

            TreeModel header:

            #ifndef TREEMODEL_H
            #define TREEMODEL_H
            
            #include <QAbstractItemModel>
            #include <QModelIndex>
            #include <QVariant>
            
            class TreeNode;
            
            class TreeModel : public QAbstractItemModel
            {
                Q_OBJECT
            
            public:
                explicit TreeModel(const QStringList &modalities, QObject *parent = 0);
                ~TreeModel();
            
                QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
                QVariant headerData(int section, Qt::Orientation orientation,
                                    int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
                QModelIndex index(int row, int column,
                                  const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
                QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
                int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
                int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
                Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
                Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE;
                Qt::DropActions supportedDragActions() const Q_DECL_OVERRIDE;
                QStringList mimeTypes() const Q_DECL_OVERRIDE;
                QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE;
                bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex & parent);
            
            private:
                TreeNode * nodeForIndex(const QModelIndex &index) const;
                void removeNode(TreeNode *node);
                void setupModelData(const QStringList &lines, TreeNode *parent);
            
                TreeNode *m_rootNode;
            };
            
            #endif // TREEMODEL_H
            

            TreeModel source:

            #include "TreeNode.h"
            #include "TreeModel.h"
            
            #include <QCoreApplication>
            #include <QStringList>
            #include <QMimeData>
            #include <QIODevice>
            #include <QDataStream>
            
            TreeModel::TreeModel(const QStringList &modalities, QObject *parent)
                : QAbstractItemModel(parent)
            {
            
                QStringList data = modalities;
            
            //    for (int i = 1 ; i < modalities.size() ; ++i)
            //        data.push_back(QString::fromStdString(modalities.at(i)));
            
                QList<QVariant> rootData;
                rootData << "Levels";
                m_rootNode = new TreeNode(rootData, 0);
                setupModelData(data, m_rootNode);
            }
            
            TreeModel::~TreeModel()
            {
                delete m_rootNode;
            }
            
            int TreeModel::columnCount(const QModelIndex &parent) const
            {
                return nodeForIndex(parent)->columnCount();
            }
            
            static const char s_treeNodeMimeType[] = "application/x-treenode";
            
            //returns the mime type
            QStringList TreeModel::mimeTypes() const
            {
                return QStringList() << s_treeNodeMimeType;
            }
            
            //receives a list of model indexes list
            QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
            {
                QMimeData *mimeData = new QMimeData;
                QByteArray data; //a kind of RAW format for datas
            
                //QDataStream is independant on the OS or proc architecture
                //serialization of C++'s basic data types, like char, short, int, char *, etc.
                //Serialization of more complex data is accomplished
                //by breaking up the data into primitive units.
                QDataStream stream(&data, QIODevice::WriteOnly);
                QList<TreeNode *> nodes;
            
                //
                foreach (const QModelIndex &index, indexes) {
                    TreeNode *node = nodeForIndex(index);
                    if (!nodes.contains(node))
                        nodes << node;
                }
                stream << QCoreApplication::applicationPid();
                stream << nodes.count();
                foreach (TreeNode *node, nodes) {
                    stream << reinterpret_cast<qlonglong>(node);
                }
                mimeData->setData(s_treeNodeMimeType, data);
                return mimeData;
            }
            
            bool TreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent)
            {
                Q_ASSERT(action == Qt::MoveAction);
                Q_UNUSED(column);
                //test if the data type is the good one
                if (!mimeData->hasFormat(s_treeNodeMimeType)) {
                    return false;
                }
                QByteArray data = mimeData->data(s_treeNodeMimeType);
                QDataStream stream(&data, QIODevice::ReadOnly);
                qint64 senderPid;
                stream >> senderPid;
                if (senderPid != QCoreApplication::applicationPid()) {
                    // Let's not cast pointers that come from another process...
                    return false;
                }
                TreeNode *parentNode = nodeForIndex(parent);
                Q_ASSERT(parentNode);
                int count;
                stream >> count;
                if (row == -1) {
                    // valid index means: drop onto item. I chose that this should insert
                    // a child item, because this is the only way to create the first child of an item...
                    // This explains why Qt calls it parent: unless you just support replacing, this
                    // is really the future parent of the dropped items.
                    if (parent.isValid())
                        row = 0;
                    else
                        // invalid index means: append at bottom, after last toplevel
                        row = rowCount(parent);
                }
                for (int i = 0; i < count; ++i) {
                    // Decode data from the QMimeData
                    qlonglong nodePtr;
                    stream >> nodePtr;
                    TreeNode *node = reinterpret_cast<TreeNode *>(nodePtr);
            
                    // Adjust destination row for the case of moving an item
                    // within the same parent, to a position further down.
                    // Its own removal will reduce the final row number by one.
                    if (node->row() < row && parentNode == node->parentNode())
                        --row;
            
                    // Remove from old position
                    removeNode(node);
            
                    // Insert at new position
                    //qDebug() << "Inserting into" << parent << row;
                    beginInsertRows(parent, row, row);
                    parentNode->insertChild(row, node);
                    endInsertRows();
                    ++row;
                }
                return true;
            }
            
            Qt::DropActions TreeModel::supportedDropActions() const
            {
                return Qt::MoveAction;
            }
            
            Qt::DropActions TreeModel::supportedDragActions() const
            {
                return Qt::MoveAction;
            }
            
            QVariant TreeModel::data(const QModelIndex &index, int role) const
            {
                if (!index.isValid())
                    return QVariant();
            
                if (role != Qt::DisplayRole)
                    return QVariant();
            
                TreeNode *node = nodeForIndex(index);
                return node->data(index.column());
            }
            
            Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
            {
                if (!index.isValid())
                    return Qt::ItemIsDropEnabled;
            
                return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
            }
            
            QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                           int role) const
            {
                if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
                    return m_rootNode->data(section);
            
                return QVariant();
            }
            
            //returns a pointer to the "index"
            TreeNode * TreeModel::nodeForIndex(const QModelIndex &index) const
            {
                if (!index.isValid())
                    return m_rootNode;
                else
                    return static_cast<TreeNode*>(index.internalPointer());
            }
            
            void TreeModel::removeNode(TreeNode *node)
            {
                const int row = node->row();
                QModelIndex idx = createIndex(row, 0, node);
                beginRemoveRows(idx.parent(), row, row);
                node->parentNode()->removeChild(row);
                endRemoveRows();
            }
            
            QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
            {
                if (!hasIndex(row, column, parent))
                    return QModelIndex();
            
                TreeNode *parentNode = nodeForIndex(parent);
            
                TreeNode *childNode = parentNode->child(row);
                if (childNode)
                    return createIndex(row, column, childNode);
                else
                    return QModelIndex();
            }
            
            QModelIndex TreeModel::parent(const QModelIndex &index) const
            {
                TreeNode *childNode = nodeForIndex(index);
                if (childNode == m_rootNode)
                    return QModelIndex();
            
                TreeNode *parentNode = childNode->parentNode();
                if (parentNode == m_rootNode)
                    return QModelIndex();
            
                return createIndex(parentNode->row(), 0, parentNode);
            }
            
            int TreeModel::rowCount(const QModelIndex &parent) const
            {
                if (parent.column() > 0)
                    return 0;
            
                TreeNode *parentNode = nodeForIndex(parent);
                return parentNode->childCount();
            }
            
            void TreeModel::setupModelData(const QStringList &lines, TreeNode *parent)
            {
                QList<TreeNode*> parents;
                QList<int> indentations;
                parents << parent;
                indentations << 0;
            
                int number = 0;
            
                while (number < lines.count()) {
                    int position = 0;
                    while (position < lines[number].length()) {
                        if (lines[number].mid(position, 1) != " ")
                            break;
                        position++;
                    }
            
                    QString lineData = lines[number].mid(position).trimmed();
            
                    if (!lineData.isEmpty()) {
                        // Read the column data from the rest of the line.
                        QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
                        QList<QVariant> columnData;
                        for (int column = 0; column < columnStrings.count(); ++column)
                            columnData << columnStrings[column];
            
                        if (position > indentations.last()) {
                            // The last child of the current parent is now the new parent
                            // unless the current parent has no children.
            
                            if (parents.last()->childCount() > 0) {
                                parents << parents.last()->child(parents.last()->childCount()-1);
                                indentations << position;
                            }
                        } else {
                            while (position < indentations.last() && parents.count() > 0) {
                                parents.pop_back();
                                indentations.pop_back();
                            }
                        }
            
                        // Append a new node to the current parent's list of children.
                        parents.last()->appendChild(new TreeNode(columnData, parents.last()));
                    }
            
                    ++number;
                }
            }
            

            And the TreeView (nothing special).
            Header:

            #ifndef TREEVIEW_H
            #define TREEVIEW_H
            
            #include <QObject>
            #include <QTreeView>
            #include "TreeModel.h"
            
            class TreeView : public QTreeView
            {
            public:
                TreeView(TreeModel *model);
                TreeView();
                void setModel(TreeModel *model);
            };
            
            #endif // TREEVIEW_H
            

            Source:

            #include "TreeView.h"
            #include "TreeModel.h"
            #include <QHeaderView>
            
            TreeView::TreeView(TreeModel *model)
            {
                setModel(model);
                TreeView();
            }
            
            TreeView::TreeView()
            {
                connect(this, &QTreeView::pressed, this, &QTreeView::expandAll);
                setDragEnabled(true);
                setAcceptDrops(true);
                resizeColumnToContents(0);
                resize(400, 500);
                setSelectionMode(QAbstractItemView::SingleSelection);
                expandAll();
                connect(this, &QTreeView::pressed, this, &QTreeView::expandAll);
                header()->setVisible(false);
                setIndentation(5);
                setEditTriggers(QAbstractItemView::EditTriggers());
            }
            
            void TreeView::setModel(TreeModel *model)
            {
                //Kick what's already selected (http://doc.qt.io/qt-4.8/qabstractitemview.html#setModel)
                QItemSelectionModel *m = selectionModel();
                delete m;
            }
            

            And the TreeNode:

            #ifndef TREENODE_H
            #define TREENODE_H
            
            #include <QList>
            #include <QVariant>
            
            class TreeNode
            {
            public:
                explicit TreeNode(const QList<QVariant> &data, TreeNode *parentNode);
                ~TreeNode();
            
                void appendChild(TreeNode *child);
                void removeChild(int row);
            
                TreeNode *child(int row) const;
                int childCount() const;
                int columnCount() const;
                QVariant data(int column) const;
                int row() const;
                TreeNode *parentNode() const;
                void insertChild(int pos, TreeNode *child);
            
            private:
                QList<TreeNode*> m_childNodes;
                QList<QVariant> m_nodeData;
                TreeNode *m_parentNode;
            };
            
            #endif // TREENODE_H
            

            source:

            #include "TreeNode.h"
            
            #include <QStringList>
            
            TreeNode::TreeNode(const QList<QVariant> &data, TreeNode *parent)
                : m_nodeData(data), m_parentNode(parent)
            {
            }
            
            TreeNode::~TreeNode()
            {
                qDeleteAll(m_childNodes);
            }
            
            void TreeNode::appendChild(TreeNode *node)
            {
                m_childNodes.append(node);
            }
            
            void TreeNode::removeChild(int row)
            {
                m_childNodes.removeAt(row);
            }
            
            TreeNode *TreeNode::child(int row) const
            {
                return m_childNodes.value(row);
            }
            
            int TreeNode::childCount() const
            {
                return m_childNodes.count();
            }
            
            int TreeNode::columnCount() const
            {
                return m_nodeData.count();
            }
            
            QVariant TreeNode::data(int column) const
            {
                return m_nodeData.value(column);
            }
            
            TreeNode *TreeNode::parentNode() const
            {
                return m_parentNode;
            }
            
            int TreeNode::row() const
            {
                if (m_parentNode)
                    return m_parentNode->m_childNodes.indexOf(const_cast<TreeNode*>(this));
            
                return 0;
            }
            
            void TreeNode::insertChild(int pos, TreeNode *child)
            {
                m_childNodes.insert(pos, child);
                child->m_parentNode = this;
            }
            
            P Offline
            P Offline
            pgathogo
            wrote on last edited by
            #5

            @Patou355 On the class TreeView method setModel, I think you might want to call the base class setModel :

            void TreeView::setModel(TreeModel* model)
            {
                QItemSelectionModel* m = selectionModel();
                delete m;
               QTreeView::setModel(model);    // <-- add this line
            }
            
            1 Reply Last reply
            0
            • U Offline
              U Offline
              userjia
              wrote on last edited by
              #6

              Thank you. Signals and solts are useful by the way.

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved