PDA

View Full Version : Practical QTreeView: Advice needed



Jojo
19th February 2006, 14:09
Hello,

I need some help using the QTreeView.
I'm having problems fitting my datastructure into QT4s Interview MVC framework.

Let my explain you what I want to do:
I have a couple of files saved in an array which looks basically like this:



struct file
{
string absoluteFilename; // Eg. "/var/foo/xx.log"
};
vector<file> _files;


Now I want to present this vector of files in a tree widget. My question is: How can I map this array of filepaths to the QAbstractItem (row, column) access? I want to show every folder as a node, and each file as a leaf.
I've thought about this problem for quite some time, but all I can think of is some really ugly mess of alot of code.
There must be some easy way to integrate a filestructure (not using QDirModel, because the files are scattered over several locations) into a tree.

Thanks for help,
Bye, Jojo :)

Codepoet
19th February 2006, 15:50
Build a "tree" of ModelIndices:
Create for every entry in your vector an ModelIndex with a parent. The root of your tree has no parent and becomes the root-item. For every other entry you have to find out its parent recursively.
The row and column indices depend on their "parent": Their model index. In every Folder they start again from 0.

The simple tree example should give you some more ideas.

Jojo
19th February 2006, 16:01
Hello,

this is what I have partially implemented.
But it totally sucks.

I have to inherit from a builtin class, override 5 virtual functions, build up another storage system for an existing model (they both need to be synced too) .. and this all to display some files in a tree? Looking at my logging output, I also see QT is calling the rowCount and data functions like crazy.

This approach is really a pita. I've tried alot of different GUI toolkits before QT, but none were so utterly complicated when it came to filling a tree with items.

Codepoet
19th February 2006, 16:23
We have a problem:
From your point of view: MVC does not allow easy display of trees
From Qt's: your data structure is not suited for display in a tree.
Further neither datastructure can be changed...

When QTreeView is to complex and / or complicated use QTreeWidget: Instead of building a tree of model indices create a tree of QTreeWidgetItems. Store in every item the index into your vector. Then you can easily synchronize both representations.
In this approach searching for an item of the vector in the tree by index is bad: So far I have no fast solution but to iterate through all items - use the path instead.

Jojo
19th February 2006, 18:00
Hello,

I've taken your advice and I have converted tree stuff to using QTreeWidgetItems now.
Though I have a nasty problem:
I can build the tree correctly, but no text or backgroundcolor is used to the shown elements. This means I see the structure of the tree not its contents.
I have no clue what might be wrong here, I suppose the QTreeWidget has a flaw of some kind.
I paste some code here, maybe you can look over it, to see if something is wrong with it. It looks right to me.




void buildTree (QTreeWidgetItem* treeWidgetItem )
{
QTreeWidgetItem* child = new QTreeWidgetItem ( QStringList() << "xxx" );
child->setText (0, "xxx"); // not used by Qt
child->setText (1, "xxx"); // not used
child->setBackgroundColor (0, QColor(255,0,0)); // not used by Qt
child->setBackgroundColor (1, QColor(255,0,0));// not used by Qt
treeWidgetItem->addChild (child);
}


void initTreewidget ( QTreeWidget* treeWidget )
{
treeWidget->setColumnCount (2);
treeWidget->setHeaderLabels ( QStringList() << "Filename" );
}


void onUpdateTreeViewSlot ( QTreeWidgetItem * fromController )
{
_treeWidget->clear ();
_treeWidget->insertTopLevelItem (0, fromController);
}



Thanks for help and looking over the code, Qt4 really is annoying when it comes to trees.

Btw: I can even crash Qt when I collapse my invisible list and scroll to the very bottom of it (scrollbars appear when the leaf is visible)
ASSERT failure in QVector<T>::at: "index out of range", file ../../include/QtCore/../../src/corelib/tools/qvector.h, line 211
zsh: 16860 abort

Codepoet
19th February 2006, 18:19
Uh - that's not normal...
Give the TreeWidgetItem a parent:


QTreeWidgetItem* child = new QTreeWidgetItem (parent);

Parent is the QTreeWidget for top-level items or a QTreeWidgetItem for nested items.
When you call setText you don't need to pass a QStringList in the ctor, but that should not be the problem here.
edit: Colors work here.

In onUpdateTreeViewSlot the fromController's parent has to be also the QTreeWidget or another item. The crash may result by Qt's memory management: The QTreeWidget takes ownership of all it's items, so you must not delete them.

If Qt behaves strange I use debug build and run it from console. Then Qt gives in a few cases some usefull output like wrong parent.

Jojo
20th February 2006, 08:13
Thank you, this helped me a lot. Setting the parent properly solved all my problems.
One question though, if I want to (partially) update the tree, I have to clear() it and reset it then, right? Sounds slow..

Codepoet
20th February 2006, 11:53
Adding a new file or folder should be easy: Just search for the parent and set it accordingly when creating the new items.
Removing entries works the same way: Search for the item and just delete it. Qt's memory management will delete all children and remove it from the tree.

But I've just realized there's another problem: Your ID's change when one removes entries in the middle of the vector. Does this happen? Do you know the performance implications for vector? list could be better...
You can avoid this ID problem by adding a new field to your file struct or always matching by the path string.

Jojo
20th February 2006, 12:59
Hello,

I know how to update my QTreeWidgetItem tree, but I don't know how to partially update the QTreeWidget. Is simply resetting the QTreeWdigetItem-root enough?
Btw, I am not using std::vector or std::string, they were just used for simplification, I am (of course) using QList ;)

Codepoet
20th February 2006, 16:01
hmm - I misunderstood you...

I am not sure what you mean by partially updating the QTreeWidget. It should repaint itself whenever you change any of its QTreeWidgetItems.

Jojo
20th February 2006, 17:55
Hello,

"it should paint itself" is exactly what I wanted to hear. This also makes it nonsense to call the update data slot very often.. excellent.

Thanks for you help :)

Jojo
20th February 2006, 20:42
It's terrible, tree problems do not stop..
Now everything is added twice to the tree. If the tree has branches, they appear duplicated too and somehow have linked selection. It follows some kind of pattern it seems, but I couldn't find the logical background behind it yet.

Here's what I'm doing. It's really nothing fancy, just some Qt tree building stuff. Maybe you find something terribly wrong with it?



/// Tree generation
QTreeWidgetItem* _treeRoot = new QTreeWidgetItem (QStringList() << "Head");

// Add *one* child
QTreeWidgetItem* child = new QTreeWidgetItem(_treeRoot);
child->setText (0, "texttext");
child->setBackgroundColor (0, QColor(255,0,0));
_treeRoot->addChild (child);

// Log the texts of each child. This *should* only print out one line, what it does, it prints out the following:
/**
FATAL (:50) - Child #0, text() == texttext
FATAL (:50) - Child #1, text() == texttext
*/
for ( int l=0; l<_treeRoot->childCount(); l++ )
{
LOG_FATAL (QString ("Child #%1, text() == %2").arg(l).arg(_treeRoot->child(l)->text(0)).toStdWString());
}


// Set tree to view ..
emit updateTreeViewSignal(_treeRoot);



/// .. view code.. called by signal above
void onUpdateTreeViewSlot ( QTreeWidgetItem * model )
{
// _treeWidget is a QTreeWidget*, created by the designer
_treeWidget->insertTopLevelItem (0, model);
}



I am again very clueless about this.
I've tried lots of stuff today, one thing I tried was passing the QTreeWidgetItem* variable named _treeRoot from the controller to the view using a signal/slot connection. In the view I said "_treeRoot = new QTreeWidgetItem (_treeWidget)". Oddly the _treeRoot parameter came back untouched (was NULL), just like before.

Thanks again for your kind help :)
Bye,
Jojo

wysota
20th February 2006, 20:51
I think you are adding each item two times (lines 5 and 8 in the code above). Passing a parent to an item should make it a child of the item, and addChild should make it a child again. Try removing one of those relations.

Jojo
20th February 2006, 21:28
This helped me, thank you very much.
Creating QTreeItemWidgets with another QTIW parameter does make it an implicit child, but calling addChild without QTIV ctor parameter does not make a parent-child relationship. Anyway, now it works .)