PDA

View Full Version : How to save QTreeWidget items in an XML file?



MathFurious
24th October 2017, 15:28
Hi,

I want to save all QTreeView items in an XML file, including child an parent, I searched but I found nothing... It is possible like below?



<root>
<Folder Name="myFolder">
<File Name="myFile" />
</Folder>
<Folder Name="mySecondFolder">
<File Name="myFile2" />
<File Name="myFile3" />
<File Name="myFile4" />
</Folder>
</root>


12640

d_stranz
24th October 2017, 17:11
It is possible like below?

Yes, of course it is possible, but you have to write the code yourself to do it. Look at QDomDocument and the related classes in the Qt XML library.

Code to do this would perform a depth-first traversal of the QTreeWidget's items. If the item has children, then it is a folder and you create a folder XML element. You then look at each of the children of this item in turn. If the child has no children, then you create a file XML element, set whatever attributes you want, and add it to the folder element you just created. If the child itself has children, then you create another folder element and examine the children of this child. Each time you come to either a file element or the end of the children, you add the element to the current parent element and move on.

Your code will basically be a top-level function to create the XML document and root element, then a recursive function that takes the current tree item pointer and current QDomElement reference and examines each child of the tree item. If the tree item has children, then create a folder element and for each child, recurse by calling the same function with the new tree item and folder element. When you reach the end of the children, add the folder to its parent and exit the recursive function. The execution will then pick up with the next child and so forth until the entire tree has been traversed.

MathFurious
19th November 2017, 15:49
Thanks!

I have another question, I do that to save QTreeWidget items in XML file:


for (int top=0; top<ui->Tree->topLevelItemCount(); top++)
{
QTreeWidgetItem *item = ui->Tree->topLevelItem(top);

QDomElement folder = doc.createElement("Folder");
folder.setAttribute("Name", item->text(0));
root.appendChild(folder);

if (item->childCount() > 0)
{
for (int child1=0; child1<item->childCount(); child1++)
{
QTreeWidgetItem *item_child = item->child(child1);

QDomElement child_folder = doc.createElement("Folder");
child_folder.setAttribute("Name", item_child->text(0));
folder.appendChild(child_folder);

if (item_child->childCount() > 0)
{
for (int child2=0; child2<item_child->childCount(); child2++)
{
QTreeWidgetItem *item_child2 = item_child->child(child2);

QDomElement child_folder2 = doc.createElement("Folder");
child_folder2.setAttribute("Name", item_child2->text(0));
child_folder.appendChild(child_folder2);

if (item_child2->childCount() > 0)
{
for (int child3=0; child3<item_child2->childCount(); child3++)
{
QTreeWidgetItem *item_child3 = item_child2->child(child3);

QDomElement child_folder3 = doc.createElement("Folder");
child_folder3.setAttribute("Name", item_child3->text(0));
child_folder2.appendChild(child_folder3);
}
}
}
}
}
}
}

But how can I create a loop. The code will save items at limit of 4 child, but the code is already too long and I don't want to create limitation of items creation...

d_stranz
20th November 2017, 17:56
Your code will basically be a top-level function to create the XML document and root element, then a recursive function that takes the current tree item pointer and current QDomElement reference and examines each child of the tree item.

Do you know what "recursive function" means? If you do, then think about how you would take your for() loop over the children and use a recursive function inside it. If you don't, then haul out your C++ textbook and read about it.

MathFurious
20th November 2017, 18:37
Thank for the reply. I do not know what is a recursive function but I will look at that!

d_stranz
21st November 2017, 01:18
A recursive function is a function that calls itself. It requires some "stopping condition" to avoid infinite recursion - at some point, the function has to return without calling itself again.

Your recursive function should look something like this (untested code):



/* This code assumes the output XML will look something like this:
<rootNode> (root of the tree)
<childNode name="abc"> (node with children) (top-level node in the tree)
<childNode name="def"/> (node with no children)
<childNode name="hij"> (node with children)
<childNode name="klm" /> (node with no children)
</childNode>
</childNode>
... (next top-level node, etc.)
</rootNode>

*/

QDomNode outputChildrenOfNode( QTreeWidgetItem * parentTreeNode, QDomDocument & doc )
{
QDomElement parentDocNode; // gets initialized as a "null" DOM node
if ( parentTreeNode != 0 )
{
parentDocNode = doc.createElement( "childNode" );
QDomAttr nameAttr = doc.createAttribute( "name" );
nameAttr.setValue( parentTreeNode->text( 0 ) );
parentDocNode.appendChild( nameAttr );

if ( parentTreeNode->childCount() > 0 ) // Stopping condition: childCount == 0
{
int nChildren = parentTreeNode->childCount();
for ( in nChild = 0; nChild < nChildren; ++nChild )
{
QTreeWidgetItem * childTreeNode = parentTreeNode->child( 0 );
QDomNode childDocNode = outputChildrenOfNode( childTreeNode, doc );
if ( !childDocNode.isNull() )
parentDocNode.appendChild( childDocNode );
}
}
)
return parentDocNode;
}


You start it off in your top-level function (code not shown) by creating the "rootNode" QDomNode. Then in a loop, you call outputChildrenOfNode() with the top-level QTreeWidgetItem nodes of the tree. After each call to this method, you append the child DOM node to the root DOM node.

If your tree items have more than one column, you'll add more attributes (with different attribute names, of course) and add them to the QDomNode created in line 19.

MathFurious
21st November 2017, 15:34
Thanks for the code. For launch the function for first time I just add it with the first top Level Item of the list at the Parent Tree Node argument?

d_stranz
21st November 2017, 18:45
For launch the function for first time I just add it with the first top Level Item of the list at the Parent Tree Node argument?

No, you have to loop over all top-level items and add each child DOM node to the root DOM node - basically lines 1-3 of your first post above:



rootNode = document.createElement( "rootNode" );
for each top-level item
childNode = outputChildrenOfNode ( top-level node[ i ] )
rootNode.appendChild( childNode )


If the QTreeView had the concept of a "root node" you could just call it with that, but it does not have that concept. Instead, it has a set of "top-level nodes" and you therefore have to loop over those and add each result to the DOM root node.

MathFurious
22nd November 2017, 15:35
I have do this:



QDomNode childNode;
for (int i=0; i<ui->Tree->topLevelItemCount(); i++)
{
childNode = outputChildrenOfNode(ui->Tree->topLevelItem(i), doc);
root.appendChild(childNode);
}


But with this list:
12682

And in the XML file it return:


<Child Name="Folder1">
<Child Name="File1"/>
<Child Name="File1"/>
<Child Name="File1"/>
</Child>

high_flyer
22nd November 2017, 16:09
Just to comment on the recursive function mentioned above:
In general it is NOT advised to use recusiveness .
It has several disadvantages, which can be very serious problem depending on the code base.
Generally it is advised to use a stack instead.
(This is not to say recursion has no place at all, but for most cases you get only drawbacks and no benefits from recursion)

d_stranz
22nd November 2017, 18:50
In general it is NOT advised to use excursiveness .

In general I agree. Recursive traversals of data structures of arbitrary depth could eventually cause problems in scaling. In this case, the OP is serializing a relatively shallow data structure for which the depth of recursion will never be too great, and a recursive method is simple and straightforward.

@MathFurious: As I said, the code I posted was written on the fly and untested, so there could be bugs. And in fact, in line 29, there -is- a bug - it should read "child( nChild )", not "child( 0 )".

MathFurious
22nd November 2017, 20:09
Everything works perfectly, thanks!

d_stranz
23rd November 2017, 17:41
By the way, if you want the XML to look like the example in your first post, all you need to do is to check to see if the current tree node has children. If it does, you make the element name "Folder"; if not, you make it "File" (instead of "Child" in all cases).

MathFurious
23rd November 2017, 17:48
Yes, I have do with icons but thank for the tip.

By the way, I want the inverse now, get XML elements to a QTreeWidget items. I create a new thread?

d_stranz
23rd November 2017, 21:02
I create a new thread?

Before you do that, why don't you study this example (https://doc.qt.io/archives/4.6/xml-streambookmarks.html) and try to write your own code based on the example? The code in the example's "XbelReader" class does almost exactly what you need to do.

After you have tried to implement something based on this, if you run into problems you can't solve (using the docs, the debugger, or whatever) then post a question in a new thread, but not before. We're here to help you with code you've written, but not to write it for you.

MathFurious
23rd November 2017, 21:08
Yes, it's true. I'll see if I can implement a function that read an XML file. Thank you.