PDA

View Full Version : QStandardItem::appendRow() segfaults



i92guboj
20th May 2014, 18:01
I've been traking this for hours. It must be something simple, it always is. But right now I can't see it.

I am following the http://qt-project.org/doc/qt-4.8/modelview.html]Model/View tutorial (http://qt-project.org/doc/qt-4.8/modelview.html) and QStandardItemModel class reference (http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html#details), but can't get this to work.

I have a QStandardItemModel derivative class, in the constructor I initialize the model and a root node:



FtpFileSystemModel::FtpFileSystemModel(QObject *parent) :
QStandardItemModel(parent)
{
ftp = new QFtp;
remote_file_list = new QList<QStandardItem*>;
root_item = invisibleRootItem();
root_item->setData("/");
......


But when I try to append a row it segfaults.



void FtpFileSystemModel::slot_list_info(QUrlInfo url_info)
{
QStandardItem *item = new QStandardItem(url_info.name());
root_item->appendRow(item); // <<<<------HERE IT SEGFAULTS!!!!!!

for(int i = 0; i < remote_file_list->count(); i++)
qDebug() << Q_FUNC_INFO << QString("row %1, data()='%2'").arg(item->index().row()).arg(item->data().toString());
}

This slot is called when a QFtp member runs list(), and it's purpose is to fill the model with info from the files in the ftp repo.

According to the debug info, there are two rows, I guess one is created by invisibleRootItem() and the other by my setData("/") sentence.

I am sure this is probably due to the fact that I don't completely understand how this model works. But I guess that's for another thread. I would be grateful if anyone can give me a hint on what could be causing this.

Thanks beforehand. :)

d_stranz
21st May 2014, 03:33
Where is "root_item" defined? Is it a member variable of the class, or is it a local variable defined in the constructor code you haven't shown us? If you've mistakenly defined it in both places, then the one in the constructor hides the member variable, so the assignment to invisibleRootItem() will be lost as soon as the constructor exits.

i92guboj
21st May 2014, 07:21
Thanks for the response.

The item is initialized using this line in the constructor, as shown in my first post.


root_item = invisibleRootItem();

At least that's what I think, since invisibleRootItem() seems to return a QStandardItem pointer. In any case, if the problem was that root_item is not initialized, the program would crash much earlier, at the next line, when I use setData() on it.

In any case, I am revising all of this. I managed to get this "someshow" working yesterday, very late (which is why I didn't write back here). But now I get a hierarchy that's different of what I thought it would be.

This models is aimed at representing a remote fs (via [q]ftp) in the fashion of QFileSystemModel for local fs's. But instead of getting something like



filename size permissions
filename size permissions
filename size permissions
filename size permissions


I was getting something more like:



filename
- size
- permissions
filename
- size
- permissions
filename
- size
- permissions


Or something like that. Can't really remember. I really don't get the hang of how trees are handled in Qt.

Before anyone says it: yes, I know I could use a QWidgetTree along with QWidgetTreeItem(s). But I definitely want to implement this in a model fashion, so that I can implement filtering and reuse the view for the directory tree and for the file listing, just like I do with QFileSystemModel.

Again, thank you for taking the time to answer. :)

d_stranz
21st May 2014, 18:45
The item is initialized using this line in the constructor, as shown in my first post.

I didn't ask where it is initialized, I asked where it was defined. If you accidentally defined the variable "root_item" as both a local variable in the constructor and as a member variable in your class, then the local definition hides the class definition, so you aren't initializing the class variable as you think.

The other possibility is that by the time your code gets to the part where you are trying to add the new item, you have managed to corrupt the memory where root_item points so it is now pointing at garbage. If you change your code to this:


void FtpFileSystemModel::slot_list_info(QUrlInfo url_info)
{
QStandardItem *item = new QStandardItem(url_info.name());
root_item = invisibleRootItem(); <<<--- new code
root_item->appendRow(item); // <<<<------HERE IT SEGFAULTS!!!!!!

for(int i = 0; i < remote_file_list->count(); i++)
qDebug() << Q_FUNC_INFO << QString("row %1, data()='%2'").arg(item->index().row()).arg(item->data().toString());
}

Does it still segfault? If it does, then you've probably trashed the memory where your FtpFileSystemModel instance lives.

One other possibility: Are you creating this FtpFileSystemModel instance on the heap (with "FtpFileSystemModel * myModel = new FtpFileSystemModel;") or are you creating it on the stack in some method ("FtpFileSystemModel myModel;")? If you are doing it the second way, then the instance has probably gone out of scope and was destroyed before the code gets to your slot. (Although destruction is supposed to disconnect signals and slots).

i92guboj
21st May 2014, 19:50
I didn't ask where it is initialized, I asked where it was defined. If you accidentally defined the variable "root_item" as both a local variable in the constructor and as a member variable in your class, then the local definition hides the class definition, so you aren't initializing the class variable as you think.

Sorry, I forgot to answer that.

The root_item is declared in the class header file. Then, initialized in the constructor.

There's no local variable with the same name, so no obfuscation.

The code has changed quite a bit, though, and now it has some other problems. I think I must really take a crash course about trees in Qt before making more noise here. There's surely some basic thing I am misunderstanding here, since everything I do only makes things worse and worse. I have read the model/view tuto, the QStandardItem[Model] manuals, the QAbstractItemModel manual, a lot of web pages, and even the sections about tree models in three different books and I can't really get the hang of it. It really doesn't matter if I use QStandardItemModel or QAbstractItemModel.

I am pretty sure the (big) problem is in my understanding of the mechanism or index() and parent() methods, in either class, and how it all glues together. I am also sure that that segfault had something to do with that. Probably I ended up with a broken tree that failed in a colorful way.

I have implemented all the imaginable trees in C and C++, Java and some other languages, but Qt is, for some reason, giving me a headache.

But enough of crying for now :P

I beg you pardon for wasting your time this way. Please, ignore this thread for now. I have some research to do, and, no doubt, I will come back to answer my own question. I am missing something really fundamental here...

Nonetheless, thank you !! :)

d_stranz
21st May 2014, 20:14
Well, generally the easiest way to implement a model to be used by a QTreeView is to implement an external, standalone data structure that contains the data you want to map to the model, using a plain old C++ data class / structure. Each of the items you want to place in the tree model contains a pointer to some node in this data tree. Thus, your model's parent() method will retrieve the pointer to the corresponding data node, go up a level in the data tree to its parent, then creates (using createIndex) a new QModelIndex where the row is the parent's index in its parent's (i.e. the grandparent of the current node) child list and the column is always zero. Set the internal data pointer of the new model index to the value of the parent node pointer and you're done.

Likewise, the index() method calls createIndex() with the row and column number requested, and assigns the data tree pointer to the index's internal pointer.

If you aren't getting a three-membered list (file, size, permission), then you are either 1) returning the wrong columnCount() for the node in the tree or 2) inserting the size and position as children of the file item (maybe by setting their parent() incorrectly in the model).

i92guboj
22nd May 2014, 17:08
Well, generally the easiest way to implement a model to be used by a QTreeView is to implement an external, standalone data structure that contains the data you want to map to the model, using a plain old C++ data class / structure. Each of the items you want to place in the tree model contains a pointer to some node in this data tree. Thus, your model's parent() method will retrieve the pointer to the corresponding data node, go up a level in the data tree to its parent, then creates (using createIndex) a new QModelIndex where the row is the parent's index in its parent's (i.e. the grandparent of the current node) child list and the column is always zero. Set the internal data pointer of the new model index to the value of the parent node pointer and you're done.

Likewise, the index() method calls createIndex() with the row and column number requested, and assigns the data tree pointer to the index's internal pointer.

If you aren't getting a three-membered list (file, size, permission), then you are either 1) returning the wrong columnCount() for the node in the tree or 2) inserting the size and position as children of the file item (maybe by setting their parent() incorrectly in the model).

Thank you so much. That was really helpful.

At the end of the day, a tree is just a tree, but I guess my previous experience with other languages played a trick or two on me this time, instead of being of help. That and the fact that QModelIndex() drives me to panic since I landed into the Qt view/model stuff as a whole some months ago :P

Right now, the code is in a state that I get output from the class into the view. That's a good starting point. I am not completely reliant about my index() and parent() method, but I am in the right track, at last.

In the right side of the image you can see the FtpFileSystemModel in action. Right now it can only show files. Hopefully it will learn to do some more stuff, given enough time :)

10376

Again, thank you :)