PDA

View Full Version : How to move table view tree item along with its children with custom data.



sumit kang
14th August 2019, 11:54
This is the header file for my tree item



class TreeItem
{
public:
explicit TreeItem( Container *data , TreeItem *parent = 0 );
~TreeItem();
TreeItem *parent();
void appendChild(TreeItem *child);

TreeItem *child(int iNumber);
int childCount() const;
int childNumber() const;
Container data() const ;
Container* GetContainer();
bool setData(Container* data , QVariant value);
void setContainer( Container* data);
bool insertChildren(int position, int count );
bool removeChildren( int position , int count );
void removeChild(int row);
void removeChild(TreeItem* itm);
std::string getChildName(int row);
std::string getName();
int row() const;
void insertChild(int pos, TreeItem *child);

private:
QList<TreeItem*> childItems;
Container* itemData;
TreeItem* parentItem;
};



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Each tree item holds a pointer reference to a container object.

when i move or copy( Cntrl Pressed) a single tree item i create a new instance of tree item and copy the data from the old item.

Currently in my code i am able to do this for single item , how to do it for a item having multiple children.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////




bool dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
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()) {
return false;
}
TreeItem *parentNode = getItem(parent);
int count;
stream >> count;
if (row == -1) {
if (parent.isValid())
row = 0;
else
row = rowCount(parent);
}
TreeItem *node;
for (int i = 0; i < count; ++i) {
qlonglong nodePtr;
stream >> nodePtr;
node = reinterpret_cast<TreeItem *>(nodePtr);
if (node->row() < row && parentNode == node->parent())
--row;

TreeItem *nodeNew = new TreeItem(node->GetContainer(), parentNode); // Create a new Data item using the node container's data.
beginInsertRows(parent, row, row);
parentNode->insertChild(row, nodeNew);
endInsertRows();
++row;

}

if (QGuiApplication::keyboardModifiers() != Qt::ControlModifier)
{
removeItem(node);
}
return true;
}

d_stranz
14th August 2019, 22:08
Currently in my code i am able to do this for single item , how to do it for a item having multiple children.

If children can have children can have children ..., then you'll need to design a pair of recursive methods to 1) create the tree inside the mime data object from the source item and 2) restore the tree from the mime data after the drop.

For the first, you basically start at the source item and write its information to the stream, then recurse for each child, appending the child's information and then recursing into its children and appending their information, and so on until you reach bottom. You then recurse back up one level and handle the next child at that level until finally you have recursed back to the source item again. Your then have a mime data object that contains the tree and all of its sub-trees, rooted at the source item. For the recursive function, at a minimum you will need to pass in a pointer to the current tree item (source or child) as well as the data stream reference so you can append to it.



// This recursive method is called from the method that creates the mime data at the beginning of the drag. It is called with the
// source TreeItem and the writable QDataStream as arguments
void buildTree( TreeItem * pItem, QDataStream & ds )
{
// write info for current item to data stream
// ...

// Now do the same for each child of the current item. If no children, then the recursion stops going deeper here
for each childItem in pItem->children()
buildTree( childItem, ds );

// done at this level. Return and go up a level
}


To restore, you do the same thing - from the data stream, restore the source item, then recurse to restore the first child's tree, and so forth until you have rebuilt the copy of the tree from the data stream. Once again, your recursive function will have to pass the data stream reference and probably the current parent item for the children you are reading in.



// This method is initially called from the dropMimeData method after the data stream is opened.
// When the recursion finally returns to the dropMimeData method, the TreeItem pointer that is returned
// will contain the complete tree that was encoded in the mime data. It can then be inserted into
// the proper place in the tree where it was dropped.
TreeItem * restoreTree( QDataStream & ds )
{
TreeItem * thisItem = new TreeItem;

// restore thisItem's info from the stream, including the count of its children
// ...

// then for each child, restore its sub-tree
for ( auto nChild = 0; nChild < childCount; ++nChild )
{
TreeItem * pChild = restoreTree( ds );
if ( pChild != nullptr )
thisItem->appendChild( pChild );
}
return thisItem;
}


You will probably have to add some bookkeeping information to the data stream when you create it so you can rebuild the tree structure. For example, for each tree item, you might write out the count of its children so you know if you have to recurse or not. You might also want to assign an ID to each node of the tree and write that out along with the ID of that node's parent, if any. That way you have a sanity check you can use to determine if you are rebuilding the tree correctly.

Google "depth first tree traversal", "depth first search", or "depth first recursion" if you want to learn more.

sumit kang
15th August 2019, 07:16
If children can have children can have children ..., then you'll need to design a pair of recursive methods to 1) create the tree inside the mime data object from the source item and 2) restore the tree from the mime data after the drop.

For the first, you basically start at the source item and write its information to the stream, then recurse for each child, appending the child's information and then recursing into its children and appending their information, and so on until you reach bottom. You then recurse back up one level and handle the next child at that level until finally you have recursed back to the source item again. Your then have a mime data object that contains the tree and all of its sub-trees, rooted at the source item. For the recursive function, at a minimum you will need to pass in a pointer to the current tree item (source or child) as well as the data stream reference so you can append to it.



// This recursive method is called from the method that creates the mime data at the beginning of the drag. It is called with the
// source TreeItem and the writable QDataStream as arguments
void buildTree( TreeItem * pItem, QDataStream & ds )
{
// write info for current item to data stream
// ...

// Now do the same for each child of the current item. If no children, then the recursion stops going deeper here
for each childItem in pItem->children()
buildTree( childItem, ds );

// done at this level. Return and go up a level
}


To restore, you do the same thing - from the data stream, restore the source item, then recurse to restore the first child's tree, and so forth until you have rebuilt the copy of the tree from the data stream. Once again, your recursive function will have to pass the data stream reference and probably the current parent item for the children you are reading in.



// This method is initially called from the dropMimeData method after the data stream is opened.
// When the recursion finally returns to the dropMimeData method, the TreeItem pointer that is returned
// will contain the complete tree that was encoded in the mime data. It can then be inserted into
// the proper place in the tree where it was dropped.
TreeItem * restoreTree( QDataStream & ds )
{
TreeItem * thisItem = new TreeItem;

// restore thisItem's info from the stream, including the count of its children
// ...

// then for each child, restore its sub-tree
for ( auto nChild = 0; nChild < childCount; ++nChild )
{
TreeItem * pChild = restoreTree( ds );
if ( pChild != nullptr )
thisItem->appendChild( pChild );
}
return thisItem;
}


You will probably have to add some bookkeeping information to the data stream when you create it so you can rebuild the tree structure. For example, for each tree item, you might write out the count of its children so you know if you have to recurse or not. You might also want to assign an ID to each node of the tree and write that out along with the ID of that node's parent, if any. That way you have a sanity check you can use to determine if you are rebuilding the tree correctly.

Google "depth first tree traversal", "depth first search", or "depth first recursion" if you want to learn more.

Thank you very much for the help.