Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. move qml treeview items
Forum Updated to NodeBB v4.3 + New Features

move qml treeview items

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
4 Posts 1 Posters 1.0k 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.
  • M Offline
    M Offline
    Marek
    wrote on 20 Nov 2019, 15:25 last edited by
    #1

    Hi all,
    I have QML TreeView with model derived from QAbstractItemModel, there is single column and I need to move items, up and down.
    My code works well when all parent items are collapsed, then I can drag up/down items no problem.
    collapsed.png
    Problem is when some view is expanded, then when I select item to move, only root items are moving and app crashes when moving item over opened branch
    expanded.png
    The question is: how do I check in QML that the item I'm moving is some child item and on cpp side moveRow should move child items of this branch?
    Code I'm using is below.

     TreeView {
            id:treeView
            anchors.fill:parent
    
            TableViewColumn {
                title: "Name"
                role: "name_role"
                width: treeView.width
            }
            model:TreeModel
            rowDelegate: Rectangle {
                id:rowDelegate
                height:50
            }
            itemDelegate: dragDelegate
    }
    Component {
            id: dragDelegate
    
            MouseArea {
                id: dragArea
                property alias item_idx:content.index
    
                property bool held: false
                height: content.height
    
                drag.target: held ? content : undefined
                drag.axis: Drag.YAxis
    
                onPressAndHold: held = true
                onReleased: held = false
    
                Rectangle {
                    id: content
                    property int index:model.index
                    anchors {
                        horizontalCenter: parent.horizontalCenter
                        verticalCenter: parent.verticalCenter
                    }
                    width: dragArea.width
                    height: 20
                    Text{
                        anchors.fill:parent
                        text: model.name_role
                        verticalAlignment: Text.AlignVCenter
                    }
    
                    border.width: 1
                    border.color: "lightsteelblue"
                    color: dragArea.held ?
                               "lightsteelblue" : "white"
                    Behavior on color { ColorAnimation { duration: 100 } }
                    radius: 2
                    Drag.active: dragArea.held
                    Drag.source: dragArea
                    Drag.hotSpot.x: width / 2
                    Drag.hotSpot.y: height / 2
                    states: State {
                        when: dragArea.held
    
                        ParentChange { target: content; parent: treeView }
                        AnchorChanges {
                            target: content
                            anchors { horizontalCenter: undefined; verticalCenter: undefined }
                        }
                    }
                }
                DropArea {
                    id:objectDropArea
                    anchors { fill: parent; margins: 10 }
    
                    onEntered: {
                        TreeModel.move(drag.source.item_idx,dragArea.item_idx)
                    }
                }
            }
     }
    cpp side:
    
    void TreeModel::move(int from, int to) {
        qDebug()<<"TreeModel::move from:"<<from<<" to:"<<to;
        int toSignal = to;
        if(from < to){
            toSignal += 1;
        }
    
        if(!beginMoveRows(QModelIndex(), from, from, QModelIndex(), toSignal)){
            return;
        }
        this->moveRow(QModelIndex(),from,QModelIndex(),toSignal);
        endMoveRows();
    }
    void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent) {
        qDebug()<<"TreeModel::setupModelData parent row:"<<parent->row();
        QList<TreeItem*> 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].at(position) != ' ')
                    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 item to the current parent's list of children.
                parents.last()->appendChild(new TreeItem(columnData, parents.last()));
            }
    
            ++number;
        }
    }
    

    Best,
    Marek

    M 1 Reply Last reply 20 Nov 2019, 18:12
    0
    • M Marek
      20 Nov 2019, 15:25

      Hi all,
      I have QML TreeView with model derived from QAbstractItemModel, there is single column and I need to move items, up and down.
      My code works well when all parent items are collapsed, then I can drag up/down items no problem.
      collapsed.png
      Problem is when some view is expanded, then when I select item to move, only root items are moving and app crashes when moving item over opened branch
      expanded.png
      The question is: how do I check in QML that the item I'm moving is some child item and on cpp side moveRow should move child items of this branch?
      Code I'm using is below.

       TreeView {
              id:treeView
              anchors.fill:parent
      
              TableViewColumn {
                  title: "Name"
                  role: "name_role"
                  width: treeView.width
              }
              model:TreeModel
              rowDelegate: Rectangle {
                  id:rowDelegate
                  height:50
              }
              itemDelegate: dragDelegate
      }
      Component {
              id: dragDelegate
      
              MouseArea {
                  id: dragArea
                  property alias item_idx:content.index
      
                  property bool held: false
                  height: content.height
      
                  drag.target: held ? content : undefined
                  drag.axis: Drag.YAxis
      
                  onPressAndHold: held = true
                  onReleased: held = false
      
                  Rectangle {
                      id: content
                      property int index:model.index
                      anchors {
                          horizontalCenter: parent.horizontalCenter
                          verticalCenter: parent.verticalCenter
                      }
                      width: dragArea.width
                      height: 20
                      Text{
                          anchors.fill:parent
                          text: model.name_role
                          verticalAlignment: Text.AlignVCenter
                      }
      
                      border.width: 1
                      border.color: "lightsteelblue"
                      color: dragArea.held ?
                                 "lightsteelblue" : "white"
                      Behavior on color { ColorAnimation { duration: 100 } }
                      radius: 2
                      Drag.active: dragArea.held
                      Drag.source: dragArea
                      Drag.hotSpot.x: width / 2
                      Drag.hotSpot.y: height / 2
                      states: State {
                          when: dragArea.held
      
                          ParentChange { target: content; parent: treeView }
                          AnchorChanges {
                              target: content
                              anchors { horizontalCenter: undefined; verticalCenter: undefined }
                          }
                      }
                  }
                  DropArea {
                      id:objectDropArea
                      anchors { fill: parent; margins: 10 }
      
                      onEntered: {
                          TreeModel.move(drag.source.item_idx,dragArea.item_idx)
                      }
                  }
              }
       }
      cpp side:
      
      void TreeModel::move(int from, int to) {
          qDebug()<<"TreeModel::move from:"<<from<<" to:"<<to;
          int toSignal = to;
          if(from < to){
              toSignal += 1;
          }
      
          if(!beginMoveRows(QModelIndex(), from, from, QModelIndex(), toSignal)){
              return;
          }
          this->moveRow(QModelIndex(),from,QModelIndex(),toSignal);
          endMoveRows();
      }
      void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent) {
          qDebug()<<"TreeModel::setupModelData parent row:"<<parent->row();
          QList<TreeItem*> 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].at(position) != ' ')
                      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 item to the current parent's list of children.
                  parents.last()->appendChild(new TreeItem(columnData, parents.last()));
              }
      
              ++number;
          }
      }
      

      Best,
      Marek

      M Offline
      M Offline
      Marek
      wrote on 20 Nov 2019, 18:12 last edited by
      #2

      There has been some progress, If I pass from QML to cpp QModelIndex then I can change order of the child items

      void TreeModel::move(QModelIndex from, QModelIndex to) {
          qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row();
          if(to.row()<0)
              return;
          if(from.row()<0)
              return;
      
          int toSignal = to.row();
          if(from.row() < to.row()){
              toSignal += 1;
          }
      
          if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){
              return;
          }
          endMoveRows();
      }
      

      Now the problem is that change is not persisten, when I open branch, change order of the items, close branch, and open again - order is reversed, so I need to change cpp model.
      Second problem: if I move item from one branch to another, bad thing happens, crash
      Best,
      Marek

      M 1 Reply Last reply 20 Nov 2019, 18:58
      0
      • M Marek
        20 Nov 2019, 18:12

        There has been some progress, If I pass from QML to cpp QModelIndex then I can change order of the child items

        void TreeModel::move(QModelIndex from, QModelIndex to) {
            qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row();
            if(to.row()<0)
                return;
            if(from.row()<0)
                return;
        
            int toSignal = to.row();
            if(from.row() < to.row()){
                toSignal += 1;
            }
        
            if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){
                return;
            }
            endMoveRows();
        }
        

        Now the problem is that change is not persisten, when I open branch, change order of the items, close branch, and open again - order is reversed, so I need to change cpp model.
        Second problem: if I move item from one branch to another, bad thing happens, crash
        Best,
        Marek

        M Offline
        M Offline
        Marek
        wrote on 20 Nov 2019, 18:58 last edited by
        #3

        Anybody? Some help would be appreciated ;)

        This is based on this example:
        https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
        I have added some simple functions to TreeItem

        void TreeItem::move(int from, int to) {
            qDebug()<<"TreeItem::move from:"<<from<<" to:"<<to;
            m_childItems.move(from,to);
        }
        void TreeItem::remove(int pos) {
            qDebug()<<"TreeItem::remove pos:"<<pos;
            m_childItems.removeAt(pos);
        }
        void TreeItem::insert(int pos,TreeItem *child) {
            qDebug()<<"TreeItem::insert pos:"<<pos<<" child:"<<child<<" count:"<<m_childItems.count();
            m_childItems.insert(pos,child);
        }
        

        So now when I'm moving items with the same parent, all is well.
        When I'm moving item to change parent, something is wrong, like in model rowCount is not refreshed, or maybe I need to rebuild some persisten index ?
        This is my code now:

        void TreeModel::move(QModelIndex from, QModelIndex to) {
            qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row();
            if(to.row()<0)
                return;
            if(from.row()<0)
                return;
        
            int toSignal = to.row();
            if(from.row() < to.row()){
                toSignal += 1;
            }
        
            if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){
                return;
            }
        
            if(from.parent().isValid()) {
                if(from.parent()==to.parent()) {
                    //move inside the same parent
                    TreeItem *parent=static_cast<TreeItem*>(from.parent().internalPointer());
                    parent->move(from.row(),to.row());
                }
                else {
                    //change parent
                    TreeItem *old_parent=static_cast<TreeItem*>(from.parent().internalPointer());
                    TreeItem *new_parent=static_cast<TreeItem*>(to.parent().internalPointer());
                    TreeItem *child=static_cast<TreeItem*>(from.internalPointer());
                    if(!old_parent || !new_parent || !child)
                        return;
                    qDebug()<<"old_parent:"<<old_parent<<" new_parent:"<<new_parent<<" child:"<<child;
                    old_parent->remove(from.row());
                    new_parent->insert(to.row(),child);
        
                }
            }
            qDebug()<<"item moved";
            endMoveRows();
        }
        

        result of moving first item (Creating a Dialog) from second branch to first branch, indenatation is wrong, and later on app crashes when I try to move again this item.

        change_parent.png

        Best,
        Marek

        M 1 Reply Last reply 21 Nov 2019, 11:13
        0
        • M Marek
          20 Nov 2019, 18:58

          Anybody? Some help would be appreciated ;)

          This is based on this example:
          https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
          I have added some simple functions to TreeItem

          void TreeItem::move(int from, int to) {
              qDebug()<<"TreeItem::move from:"<<from<<" to:"<<to;
              m_childItems.move(from,to);
          }
          void TreeItem::remove(int pos) {
              qDebug()<<"TreeItem::remove pos:"<<pos;
              m_childItems.removeAt(pos);
          }
          void TreeItem::insert(int pos,TreeItem *child) {
              qDebug()<<"TreeItem::insert pos:"<<pos<<" child:"<<child<<" count:"<<m_childItems.count();
              m_childItems.insert(pos,child);
          }
          

          So now when I'm moving items with the same parent, all is well.
          When I'm moving item to change parent, something is wrong, like in model rowCount is not refreshed, or maybe I need to rebuild some persisten index ?
          This is my code now:

          void TreeModel::move(QModelIndex from, QModelIndex to) {
              qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row();
              if(to.row()<0)
                  return;
              if(from.row()<0)
                  return;
          
              int toSignal = to.row();
              if(from.row() < to.row()){
                  toSignal += 1;
              }
          
              if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){
                  return;
              }
          
              if(from.parent().isValid()) {
                  if(from.parent()==to.parent()) {
                      //move inside the same parent
                      TreeItem *parent=static_cast<TreeItem*>(from.parent().internalPointer());
                      parent->move(from.row(),to.row());
                  }
                  else {
                      //change parent
                      TreeItem *old_parent=static_cast<TreeItem*>(from.parent().internalPointer());
                      TreeItem *new_parent=static_cast<TreeItem*>(to.parent().internalPointer());
                      TreeItem *child=static_cast<TreeItem*>(from.internalPointer());
                      if(!old_parent || !new_parent || !child)
                          return;
                      qDebug()<<"old_parent:"<<old_parent<<" new_parent:"<<new_parent<<" child:"<<child;
                      old_parent->remove(from.row());
                      new_parent->insert(to.row(),child);
          
                  }
              }
              qDebug()<<"item moved";
              endMoveRows();
          }
          

          result of moving first item (Creating a Dialog) from second branch to first branch, indenatation is wrong, and later on app crashes when I try to move again this item.

          change_parent.png

          Best,
          Marek

          M Offline
          M Offline
          Marek
          wrote on 21 Nov 2019, 11:13 last edited by
          #4

          I have corrected move function a bit, it almost work ;) the problem is that some strange "artifact" is left afte move with change parent operation

          Here is the code:

          void TreeModel::move(QModelIndex from, QModelIndex to) {
              qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row();
          
              int toSignal = to.row();
              if(from.row() < to.row()){
                  toSignal += 1;
              }
          
              if(from.parent().isValid()) {
                  if(from.parent()==to.parent()) {
                      //move inside the same parent
                      qDebug()<<"TreeModel::move move inside parent";
                      if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){
                          return;
                      }
                      TreeItem *parent=static_cast<TreeItem*>(from.parent().internalPointer());
                      parent->move(from.row(),to.row());
                      endMoveRows();
                  }
                  else {
                      //change parent
          
                      TreeItem *old_parent=static_cast<TreeItem*>(from.parent().internalPointer());
                      TreeItem *new_parent=static_cast<TreeItem*>(to.parent().internalPointer());
                      TreeItem *child=static_cast<TreeItem*>(from.internalPointer());
                      if(!old_parent || !new_parent || !child)
                          return;
                      qDebug()<<"old_parent:"<<old_parent<<" new_parent:"<<new_parent<<" child:"<<child;
          
                      emit beginRemoveRows(from.parent(),from.row(),from.row());
                      old_parent->remove(from.row());
                      emit endRemoveRows();
          
                      emit beginInsertRows(to.parent(),to.row(),to.row());
                      child->setParent(new_parent);
                      new_parent->insert(to.row(),child);
                      emit endInsertRows();
                  }
              }
              else {
                  qDebug()<<"TreeModel::move parent invalid";
              }
          }
          

          Here is the result:
          change_parent2.png
          And some debug from this operation:

          TreeModel::move from: 0  to: 1
          old_parent: 0x55ef91136820  new_parent: 0x55ef911363d0  child: 0x55ef91136930
          TreeItem::remove pos: 0
          TreeItem::insert pos: 1  child: 0x55ef91136930  count: 3
          TreeModel::move from: -1  to: 1
          TreeModel::move parent invalid
          

          I was moving "Creating a Dialog" from first position in second branch to first branch, at the end there is some move operation with invalid parent index, I don't know where this comes from...

          Best,
          Marek

          1 Reply Last reply
          1

          1/4

          20 Nov 2019, 15:25

          • Login

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